פתרון בעיות

נתיב הבינארי של Python חסר

הודעת השגיאה:

ERROR: An error occurred during the fetch of repository 'local_execution_config_python':
  Traceback (most recent call last):
       File "/sandbox_path/external/org_tensorflow/third_party/py/python_configure.bzl", line 208
               get_python_bin(repository_ctx)
    ...
Repository command failed

בדרך כלל מציין ש-Bazel לא מצליח למצוא את קובץ הבינארי המקומי של Python. כדי לפתור את הבעיה, צריך קודם למצוא את הקובץ הבינארי של Python ואז להוסיף את --action_env PYTHON_BIN_PATH=<path to python binary> לפקודה של Bazel. לדוגמה, אפשר להשתמש בפקודה הבאה כדי לעבור לשימוש ב-python3 הבינארי שמוגדר כברירת מחדל במערכת:

bazel build -c opt \
  --define MEDIAPIPE_DISABLE_GPU=1 \
  --action_env PYTHON_BIN_PATH=$(which python3) \
  mediapipe/examples/desktop/hello_world

חסרות חבילות Python הנדרשות

הודעת השגיאה:

ImportError: No module named numpy
Is numpy installed?

בדרך כלל מציין שחבילות Python מסוימות לא מותקנות. כדי להתקין את החבילות האלה, מריצים את הפקודה pip install או את הפקודה pip3 install, בהתאם לגרסה הבינארית של Python.

אי אפשר לאחזר מאגרי יחסי תלות מרוחקים

הודעת השגיאה:

ERROR: An error occurred during the fetch of repository 'org_tensorflow':
   java.io.IOException: Error downloading [https://mirror.bazel.build/github.com/tensorflow/tensorflow/archive/77e9ffb9b2bfb1a4f7056e62d84039626923e328.tar.gz, https://github.com/tensorflow/tensorflow/archive/77e9ffb9b2bfb1a4f7056e62d84039626923e328.tar.gz] to /sandbox_path/external/org_tensorflow/77e9ffb9b2bfb1a4f7056e62d84039626923e328.tar.gz: Tried to reconnect at offset 9,944,151 but server didn't support it

or

WARNING: Download from https://storage.googleapis.com/mirror.tensorflow.org/github.com/bazelbuild/rules_swift/releases/download/0.12.1/rules_swift.0.12.1.tar.gz failed: class java.net.ConnectException Connection timed out (Connection timed out)

בדרך כלל מציינת ש-Bazel לא מצליח להוריד את מאגרי התלות הנדרשים ל-MediaPipe. ל-MediaPipe יש כמה מאגרי יחסי תלות שמתארחים באתרים של Google. באזורים מסוימים, יכול להיות שתצטרכו להגדיר שרת proxy ברשת או להשתמש ב-VPN כדי לגשת למשאבים האלה. יכול להיות שתצטרכו גם לצרף את הערך --host_jvm_args "-DsocksProxyHost=<ip address> -DsocksProxyPort=<port number>" לפקודת Bazel. פרטים נוספים זמינים בבעיה הזו ב-GitHub.

אם לדעתך זו לא בעיה ברשת, יכול להיות שחלק מהמשאבים לא זמינים באופן זמני. עליך להריץ את הפקודה bazel clean --expunge ולנסות שוב מאוחר יותר. אם הבעיה נמשכת, יש לשלוח דיווח בנושא ב-GitHub עם הודעת השגיאה המפורטת.

הגדרה שגויה של MediaPipe OpenCV

הודעת השגיאה:

error: undefined reference to 'cv::String::deallocate()'
error: undefined reference to 'cv::String::allocate(unsigned long)'
error: undefined reference to 'cv::VideoCapture::VideoCapture(cv::String const&)'
...
error: undefined reference to 'cv::putText(cv::InputOutputArray const&, cv::String const&, cv::Point, int, double, cv::Scalar, int, int, bool)'

בדרך כלל מציין ש-OpenCV לא מוגדר כראוי ל-MediaPipe. בקטע 'התקנה של OpenCV ו-FFmpeg' בקטע התקנה מוסבר איך לשנות את הקבצים WORKSPACE ו-linux_opencv/macos_opencv/windows_opencv.BUILD של MediaPipe עבור ספריות ה-OpenCV המקומיות. הבעיה הזו ב-GitHub יכולה לעזור גם כן.

כשל בהתקנה של Python pip

הודעת השגיאה:

ERROR: Could not find a version that satisfies the requirement mediapipe
ERROR: No matching distribution found for mediapipe

אחרי הפעלת pip install mediapipe, בדרך כלל סימן לכך שאין במערכת שלכם גרסת MediaPipe Python מתאימה. חשוב לדעת ש-MediaPipe Python PyPI תומך באופן רשמי בגרסת 64-ביט של Python במערכות ההפעלה הבאות:

  • x86_64 Linux
  • x86_64 macOS מגרסה 11.0 ואילך
  • arm64 macOS מגרסה 11.0 ואילך
  • Windows amd64

אם מערכת ההפעלה נתמכת כרגע והשגיאה הזו עדיין מופיעה, צריך לוודא שגם קובץ ה-Python וגם קובץ ה-pip הבינארי הם לגרסאות Python הנתמכות ב-MediaPipe. לחלופין, תוכלו לבנות את חבילת Python של MediaPipe באופן מקומי לפי ההוראות כאן.

כשל בטעינת Python DLL ב-Windows

הודעת השגיאה:

ImportError: DLL load failed: The specified module could not be found

בדרך כלל מציין שבמערכת Windows המקומית חסרים חבילות Visual C++ לחלוקה חוזרת ו/או קובצי DLL של Visual C++ בסביבת זמן ריצה. אפשר לפתור את הבעיה על ידי התקנה של vc_redist.x64.exe הרשמית או התקנה של חבילת Python‏ 'msvc-runtime' על ידי הפעלת

$ python -m pip install msvc-runtime

חשוב לדעת שחבילת Python‏ 'msvc-runtime' לא פורסמה על ידי Microsoft ולא מתבצעת על ידה תחזוקה שלה.

לא נמצאה שיטה מקורית

הודעת השגיאה:

java.lang.UnsatisfiedLinkError: No implementation found for void com.google.wick.Wick.nativeWick

בדרך כלל מציין שספרייה מקומית נדרשת, כמו /libwickjni.so, לא נטענה או לא נכללה ביחסי התלות של האפליקציה, או שלא ניתן למצוא אותה מסיבה כלשהי. חשוב לזכור ש-Java דורשת שכל ספרייה מקומית תיטען באופן מפורש באמצעות הפונקציה System.loadLibrary.

לא נמצא מחשבון רשום

הודעת השגיאה:

No registered object with name: OurNewCalculator; Unable to find Calculator "OurNewCalculator"

בדרך כלל מציין שקיימת הפניה ל-OurNewCalculator לפי שם ב-CalculatorGraphConfig, אבל יעד הספרייה של OurNewCalculator לא מקושר לקובץ הבינארי של האפליקציה. כשמוסיפים מחשבון חדש לתרשים של מחשבון, צריך להוסיף את המחשבון הזה גם כיחס תלות ב-build של האפליקציות שמשתמשות בתרשים של המחשבון.

השגיאה הזו מתגלה בזמן הריצה כי התרשימים של המחשבונים מפנים למחשבונים שלהם לפי שם דרך השדה CalculatorGraphConfig::Node:calculator. כשמקשרים את הספרייה של מחשבון לאפליקציה בינארית, המחשבון רשום באופן אוטומטי לפי שם באמצעות המאקרו REGISTER_CALCULATOR באמצעות הספרייה registration.h. שימו לב ש-REGISTER_CALCULATOR יכול לרשום מחשבון עם קידומת של מרחב שמות, זהה למרחב השמות שלו ב-C++‎. במקרה כזה, גם בתרשים של המחשבון צריך להשתמש באותה תחילית של מרחב השמות.

שגיאת 'אין מספיק זיכרון'

זיכרון שנצרך במלואו יכול להיות סימן לכך שמצטברות יותר מדי חבילות בתוך תרשים MediaPipe שפועל. יכולות להיות לכך כמה סיבות, למשל:

  1. חלק מהמחשבונים בתרשים פשוט לא יכולים לעמוד בקצב ההגעה של חבילות מזרם קלט בזמן אמת, כמו מצלמת וידאו.
  2. חלק מהמחשבונים ממתינים לחבילות שלא יגיעו אף פעם.

בבעיה (1), יכול להיות שיהיה צורך להשליך חלק מהמנות הישנות כדי לעבד את המנות העדכניות יותר. טיפים נוספים זמינים במאמר: How to process realtime input streams.

בבעיה (2), יכול להיות שחסרות מנות באחד מזרמי הקלט מסיבה כלשהי. יכול להיות שהמכשיר או המחשבון מוגדרים באופן שגוי או שהם יוצרים חבילות רק באופן ספורי. כתוצאה מכך, מחשבים במורד הזרם עשויים להמתין להרבה חבילות שלא יגיעו אף פעם, וכתוצאה מכך חבילות יצטברו בחלק ממקורות הקלט שלהם. כדי לטפל בבעיות כאלה, ב-MediaPipe נעשה שימוש ב'גבולות חותמת זמן'. טיפים נוספים זמינים במאמר How to process realtime input streams.

ההגדרה של MediaPipe‏ CalculatorGraphConfig::max_queue_size מגבילה את מספר החבילות שנכנסות לתור בכל מקור קלט על ידי צמצום הקלט לתרשים. במקרים של מקורות קלט בזמן אמת, מספר החבילות בתור של מקור הקלט צריך להיות כמעט תמיד אפס או אחת. אם לא, יכול להיות שתופיע הודעת האזהרה הבאה:

Resolved a deadlock by increasing max_queue_size of input stream

אפשר גם להגדיר את ההגדרה CalculatorGraphConfig::report_deadlock כך שההרצה של הגרף תיכשל והנעילה המוחלפת תופיע כשגיאה, כך ש-max_queue_size יפעל כמגבלה על שימוש בזיכרון.

גרפים שנתקעים

אפליקציות רבות יקראו ל-CalculatorGraph::CloseAllPacketSources ול-CalculatorGraph::WaitUntilDone כדי לסיים או להשעות את הביצוע של גרף MediaPipe. המטרה היא לאפשר למחשבונים או לחבילות בהמתנה להשלים את העיבוד, ולאחר מכן לכבות את התרשים. אם הכל יתנהל כשורה, כל המקורות בתרשים יגיעו אל Timestamp::Done, כל המחשבים יגיעו אל CalculatorBase::Close ואז CalculatorGraph::WaitUntilDone יושלם בהצלחה.

אם חלק מהמחשבים או מהזרמים לא יכולים להגיע למצב Timestamp::Done או CalculatorBase::Close, אפשר להפעיל את השיטה CalculatorGraph::Cancel כדי לסיים את ההרצה של הגרף בלי להמתין להשלמת כל המחשבים והחבילות בהמתנה.

תזמון הפלט לא אחיד

חלק מהגרפים של MediaPipe בזמן אמת יוצרים סדרה של פריימים של סרטון, שאפשר לצפות בהם כאפקט וידאו או כאבחון של סרטון. לפעמים, תרשים של MediaPipe ייצור את הפריימים האלה באשכול, למשל כשמספר פריימים של פלט יופקו מאותו אשכול של פריימים של קלט. אם הפלט מוצג בזמן שהוא נוצר, חלק מהפריימים של הפלט מוחלפים באופן מיידי בפריימים מאוחרים יותר באותו אשכול, וכך קשה לראות ולשפוט את התוצאות באופן חזותי. במקרים כאלה, אפשר לשפר את התצוגה החזותית של הפלט על ידי הצגת המסגרות במרווחים שווים בזמן אמת.

כדי לטפל בתרחיש לדוגמה הזה, ב-MediaPipe מתבצע מיפוי של חותמות זמן לנקודות בזמן אמת. כל חותמת זמן מציינת זמן במיקרו-שניות, ומחשבון כמו LiveClockSyncCalculator יכול לעכב את הפלט של החבילות כך שיתאים לחותמות הזמן שלהן. מחשבון כזה מתאים את תזמון הפלט כך:

  1. הזמן בין הפלטים תואם לזמן בין חותמות הזמן בצורה הקרובה ביותר האפשרית.
  2. הפלט נוצר עם העיכוב הקצר ביותר האפשרי.

CalculatorGraph מאחר אחרי הקלט

בגרפים רבים של MediaPipe בזמן אמת, זמן אחזור קצר הוא יעד. ‏MediaPipe תומך בעיבוד מקבילי בסגנון צינור עיבוד נתונים (pipeline), כדי להתחיל את העיבוד של כל חבילה בהקדם האפשרי. בדרך כלל, זמן האחזור הנמוך ביותר האפשרי הוא הזמן הכולל שנדרש לכל מחשבון לאורך 'נתיב קריטי' של מחשבונים עוקבים. זמן האחזור של תרשים MediaPipe עשוי להיות ארוך יותר מהזמן האידיאלי בגלל עיכובים שנוצרים כדי להציג את המסגרות במרווחי זמן שווים, כפי שמתואר בקטע תזמון הפלט לא אחיד.

אם חלק מהמחשבים בתרשים לא יוכלו לעמוד בקצב של מקורות הקלט בזמן אמת, זמן האחזור ימשיך לעלות ויהיה צורך להשליך חלק מחבילות הקלט. השיטה המומלצת היא להשתמש במחשבים של MediaPipe שמיועדים במיוחד למטרה הזו, כמו FlowLimiterCalculator כפי שמתואר במאמר How to process realtime input streams.

מעקב אחר הקלט של המחשבון וסילוקים של חותמות זמן

כדי לנפות באגים במחשבים של MediaPipe, לרוב צריך להבין לעומק את זרימת הנתונים ואת הסנכרון של חותמות הזמן. חבילות נכנסות למחשבים מתאימים נשמרות קודם במאגרים של תורי קלט לכל שידור, כדי שהן יסונכרנו על ידי InputStreamHandler שהוקצה. המשימה InputStreamHandler קובעת את קבוצת ה-packet של הקלט עבור חותמת זמן מוסכמת, שמעבירה את המחשבון למצב 'מוכן', ולאחר מכן מפעילה קריאה ל-Calculator::Process עם קבוצת ה-packet שהוגדרה כקלט.

אפשר להשתמש ב-DebugInputStreamHandler כדי לעקוב אחרי חבילות נכנסות וסימוני זמן חותמת בזמן אמת בפלט LOG(INFO) של האפליקציה. אפשר להקצות אותו למחשבונים ספציפיים דרך input_stream_handler של המחשבון, או ברמת התרשים באמצעות השדה input_stream_handler של CalculatorGraphConfig.

במהלך ביצוע התרשים, חבילות נכנסות יוצרות הודעות ביומן שמציגות את חותמת הזמן ואת הסוג של החבילה, ואחריה את המצב הנוכחי של כל תורי הקלט:

[INFO] SomeCalculator: Adding packet (ts:2, type:int) to stream INPUT_B:0:input_b
[INFO] SomeCalculator: INPUT_A:0:input_a num_packets: 0 min_ts: 2
[INFO] SomeCalculator: INPUT_B:0:input_b num_packets: 1 min_ts: 2

בנוסף, היא מאפשרת לעקוב אחרי אירועי הסדר של חותמות זמן (במקרה שמופעלת DefaultInputStreamHandler). כך תוכלו לזהות עלייה בלתי צפויה במגבלה של חותמת הזמן במקורות הקלט, שמובילה לקריאה ל-Calculator::Process עם קבוצת קלט חלקית, וכתוצאה מכך ליצירת חבילות ריקות במקורות קלט (שעשויים להיות נדרשים).

תרחיש לדוגמה:

node {
  calculator: "SomeCalculator"
  input_stream: "INPUT_A:a"
  input_stream: "INPUT_B:b"
  ...
}

מחשבון עם שני מקורות קלט, שמקבל חבילה נכנסת עם חותמת זמן 1 בשידור A ואחריה חבילה קלט עם חותמת זמן 2 בשידור B. כשהגבול של חותמת הזמן עולה ל-2 בשידור B עם חבילה בהמתנה של קלט בשידור A בחותמת הזמן 1, הקריאה Calculator::Process מופעלת עם קבוצת קלט חלקית עבור חותמת הזמן 1. במקרה כזה, הפלט של DefaultInputStreamHandler:

[INFO] SomeCalculator: Filled input set at ts: 1 with MISSING packets in input streams: INPUT_B:0:input_b.

VLOG הוא החבר שלכם

ב-MediaPipe נעשה שימוש ב-VLOG במקומות רבים כדי לתעד אירועים חשובים למטרות ניפוי באגים, בלי להשפיע על הביצועים אם הרישום ביומן לא מופעל.

מידע נוסף על VLOG זמין ב-abseil VLOG

חשוב לזכור ש-VLOG יכול לגרום לספאם אם מפעילים אותו באופן גלובלי, למשל (באמצעות הדגל --v). הדגל --vmodule של הפתרון שמאפשר להגדיר רמות שונות לקבצי מקור שונים.

במקרים שבהם אי אפשר להשתמש ב---v או ב---vmodule (למשל, הפעלת אפליקציה ל-Android), אפשר להשתמש ב-MediaPipe כדי להגדיר החלפה של דגלים של VLOG --v או --vmodule למטרות ניפוי באגים, שתחול כשיוצרים את CalculatorGraph.

ביטולים:

  • MEDIAPIPE_VLOG_V: מגדירים את הערך שאתם מספקים ל---v ומספקים אותו
  • MEDIAPIPE_VLOG_VMODULE: מגדירים את הערך שאתם מספקים ל---vmodule ומספקים אותו

כדי להגדיר שינויים מברירת המחדל, מוסיפים את הפרמטר: --copt=-DMEDIAPIPE_VLOG_VMODULE=\"*calculator*=5\"

עם דפוסי המודול הרצויים ורמות VLOG (פרטים נוספים על --vmodule זמינים ב-abseil VLOG) בפקודת ה-build.

חשוב לזכור: הוספת הקוד שלמעלה לפקודת ה-build תגרום ליצירה מחדש של קובץ הבינארי כולו, כולל יחסי התלות. מכיוון שהחלפות של VLOG קיימות רק למטרות ניפוי באגים, קל יותר פשוט לשנות את vlog_overrides.cc ולהוסיף את MEDIAPIPE_VLOG_V/VMODULE בחלק העליון.

דגלים לא נתמכים במהלך ה-build

אם אתם משתמשים ב-Clang בגרסה 18 ואילך, יכול להיות שתצטרכו להשבית אופטימיזציות מסוימות של המהדר בצד העורפי של המעבד.

כדי להשבית את התמיכה ב-avxvnniint8, מוסיפים את הפרטים הבאים ל-.bazelrc:

build --define=xnn_enable_avxvnniint8=false