问题排查

缺少 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 软件包。请根据您的 Python 二进制版本运行 pip installpip3 install 以安装这些软件包。

未能提取远程依赖项代码库

错误消息:

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 网站托管的依赖项代码库。在某些地区,您可能需要设置网络代理或使用 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”部分,了解如何修改 MediaPipe 的 WORKSPACE 和 linux_opencv/macos_opencv/windows_opencv.BUILD 文件以适应本地 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 3.7 到 3.10:

  • x86_64 Linux
  • x86_64 macOS 10.15 及更高版本
  • amd64 Windows

如果当前操作系统受支持,但您仍看到此错误,请确保 Python 和 pip 二进制文件均适用于 Python 3.7 到 3.10。否则,请考虑按照此处的说明在本地构建 MediaPipe Python 软件包。

Windows 上 Python DLL 加载失败

错误消息:

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

通常表示本地 Windows 系统缺少 Visual C++ 可再分发软件包和/或 Visual C++ 运行时 DLL。您可以通过以下任一方式解决此问题:安装官方 vc_redist.x64.exe,或运行以下命令安装“msvc-runtime”Python 软件包

$ python -m pip install msvc-runtime

请注意,“msvc-runtime”Python 软件包并非由 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"

通常表示 OurNewCalculatorCalculatorGraphConfig 中按名称引用,但 OurNewCalculator 的库目标尚未关联到应用二进制文件。向计算器图表添加新计算器时,还必须将该计算器添加为使用计算器图表的应用的构建依赖项。

此错误会在运行时被捕获,因为计算器图表通过字段 CalculatorGraphConfig::Node:calculator 按名称引用其计算器。将计算器的库链接到应用二进制文件后,系统会使用 registration.h 库通过 REGISTER_CALCULATOR 宏按名称自动注册计算器。请注意,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::CloseAllPacketSourcesCalculatorGraph::WaitUntilDone 来完成或暂停 MediaPipe 图的执行。此操作的目标是允许所有待处理的计算器或数据包完成处理,然后关闭图表。如果一切顺利,图中的每个数据流都会达到 Timestamp::Done,每个计算器都会达到 CalculatorBase::Close,然后 CalculatorGraph::WaitUntilDone 将成功完成。

如果某些计算器或数据流无法达到状态 Timestamp::DoneCalculatorBase::Close,则可以调用方法 CalculatorGraph::Cancel 来终止图的运行,而无需等待所有待处理的计算器和数据包完成。

输出时间不均匀

某些实时 MediaPipe 图表会生成一系列视频帧,以便作为视频效果或视频诊断内容进行查看。有时,MediaPipe 图会以集群的形式生成这些帧,例如,从同一组输入帧推断出多个输出帧时。如果输出在生成时显示,则某些输出帧会立即被同一集群中的后续帧替换,这会导致结果难以直观地查看和评估。在这种情况下,可以通过实时均匀间隔地呈现帧来改进输出可视化效果。

MediaPipe 通过将时间戳实时映射到点来解决此用例。每个时间戳都表示一个微秒级时间,并且 LiveClockSyncCalculator 等计算器可以延迟数据包的输出以匹配其时间戳。此类计算器会调整输出的时间,以便:

  1. 输出之间的时间与时间戳之间的时间尽可能接近。
  2. 输出以最短的延迟时间生成。

CalculatorGraph 滞后于输入

对于许多实时 MediaPipe 图表,低延迟是目标。MediaPipe 支持“流水线”式并行处理,以便尽早开始处理每个数据包。通常,最短的延迟时间是每个计算器沿着连续计算器的“关键路径”所需的总时间。由于在均匀间隔的时间显示帧时引入了延迟,MediaPipe 图的延迟时间可能会比理想情况更长,如输出时间不均匀中所述。

如果图表中的某些计算器无法跟上实时输入流的节奏,则延迟时间将继续增加,并且必须丢弃一些输入数据包。建议的方法是使用专为此目的设计的 MediaPipe 计算器,例如 FlowLimiterCalculator,如 How to process realtime input streams 中所述。

监控计算器输入和时间戳结算

调试 MediaPipe 计算器通常需要深入了解数据流和时间戳同步。发送给计算器的传入数据包会先缓冲在每个串流的输入队列中,以便由分配的 InputStreamHandler 进行同步。InputStreamHandler 作业用于确定已确定时间戳的输入数据包集,这会将计算器置于“准备就绪”状态,然后触发 Calculator::Process 调用,并将确定的数据包集作为输入。

DebugInputStreamHandler 可用于在应用的 LOG(INFO) 输出中实时跟踪传入数据包和时间戳结算。您可以通过计算器的 input_stream_handler 将其分配给特定计算器,也可以通过 CalculatorGraphConfig 的 input_stream_handler 字段在全球范围内对其进行图表化处理。

在图表执行期间,传入数据包会生成 LOG 消息,其中包含数据包的时间戳和类型,以及所有输入队列的当前状态:

[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"
  ...
}

假设有一个具有两个输入的计算器,在数据流 A 上接收时间戳为 1 的传入数据包,然后在数据流 B 上接收时间戳为 2 的输入数据包。时间戳边界在数据流 B 上增加到 2,同时在时间戳 1 时刻数据流 A 上有待处理的输入数据包,这会触发 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,即可更快地完成操作。

构建期间不支持的标志

如果您使用的是 Clang 18 或更低版本,则可能需要在我们的 CPU 后端停用某些编译器优化。

如需停用对 avxvnniint8 的支持,请将以下代码添加到您的 .bazelrc

build --define=xnn_enable_avxvnniint8=false