4242}
4343
4444
45+ import contextlib
46+
47+
48+ @contextlib .contextmanager
49+ def _suppress_native_stderr (active = True ):
50+ """
51+ Temporarily redirect the process's stderr (fd 2) to /dev/null.
52+
53+ MediaPipe logs from its C++/GL layer (EGL init, absl INFO/WARNING) go straight to
54+ the stderr file descriptor and can't be silenced from Python logging. This redirects
55+ fd 2 around the MediaPipe run so those messages don't clutter notebooks. No-op when
56+ ``active`` is False (use ``quiet=False`` to see the native logs for debugging).
57+ """
58+ if not active :
59+ yield
60+ return
61+ import sys
62+ sys .stderr .flush ()
63+ devnull = os .open (os .devnull , os .O_WRONLY )
64+ saved = os .dup (2 )
65+ try :
66+ os .dup2 (devnull , 2 )
67+ yield
68+ finally :
69+ os .dup2 (saved , 2 )
70+ os .close (devnull )
71+ os .close (saved )
72+
73+
4574def _pose_canvas_and_colors (frame , overlay , background ):
4675 """Return (canvas, line_color, marker_color) in BGR for drawing the pose."""
4776 if overlay :
@@ -64,6 +93,7 @@ def pose(
6493 overlay = True ,
6594 background = 'black' ,
6695 convert = True ,
96+ quiet = True ,
6797 save_average_pose = True ,
6898 save_trajectories = True ,
6999 transparent_trajectories = None ,
@@ -119,6 +149,9 @@ def pose(
119149 convert (bool, optional): If True (default), non-AVI input is first converted to an all-intra
120150 MJPEG `.avi` (cached as ``self.as_avi``) for frame-accurate decoding. Set to False to read the
121151 source file directly — faster and no extra file, suitable when your mp4 decodes reliably.
152+ quiet (bool, optional): MediaPipe only. If True (default), suppress MediaPipe's native C++/GL
153+ console logs (EGL init, absl INFO/WARNING, GPU-delegate messages) during inference. Set to
154+ False to see them for debugging.
122155 target_name_video (str, optional): Target output name for the video. Defaults to None (which
123156 assumes that the input filename with the suffix "_pose" should be used).
124157 save_average_pose (bool, optional): Whether to also render an image of the average pose over
@@ -155,23 +188,28 @@ def pose(
155188
156189 # --- MediaPipe backend ---------------------------------------------------
157190 # Explicit MediaPipe request, or auto-preference: when GPU is requested for an
158- # OpenPose model but OpenCV has no CUDA backend, prefer MediaPipe (which can use
159- # its own GPU delegate) instead of silently dropping to CPU OpenPose.
191+ # OpenPose model but OpenCV has no CUDA backend, fall back to the (fast, reliable)
192+ # MediaPipe pose backend instead of CPU OpenPose. We use the CPU delegate here:
193+ # MediaPipe's GPU delegate is the OpenGL-ES path (the integrated GPU on Linux, not
194+ # an NVIDIA card) and is fragile, so it is reserved for explicit model='mediapipe',
195+ # device='gpu' requests.
160196 use_mediapipe = model .lower () == 'mediapipe'
197+ mediapipe_device = device
161198 if not use_mediapipe and device .lower () == 'gpu' and not in_colab () and get_cuda_device_count () <= 0 :
162199 if _mediapipe_available ():
163200 print (
164201 f"GPU requested but OpenCV has no CUDA backend; switching from '{ model } ' to the "
165- "MediaPipe pose backend for GPU acceleration (33 landmarks ).\n "
202+ "MediaPipe pose backend (CPU/XNNPACK — fast and reliable ).\n "
166203 + cuda_unavailable_reason ()
167204 )
168205 use_mediapipe = True
206+ mediapipe_device = 'cpu'
169207 # else: fall through to the OpenPose path, which will warn and use CPU.
170208
171209 if use_mediapipe :
172210 return _pose_mediapipe (
173211 self ,
174- device = device ,
212+ device = mediapipe_device ,
175213 threshold = threshold ,
176214 save_data = save_data ,
177215 data_format = data_format ,
@@ -180,6 +218,7 @@ def pose(
180218 overlay = overlay ,
181219 background = background ,
182220 convert = convert ,
221+ quiet = quiet ,
183222 save_average_pose = save_average_pose ,
184223 save_trajectories = save_trajectories ,
185224 transparent_trajectories = transparent_trajectories ,
@@ -581,6 +620,7 @@ def _pose_mediapipe(
581620 overlay = True ,
582621 background = 'black' ,
583622 convert = True ,
623+ quiet = True ,
584624 save_average_pose = True ,
585625 save_trajectories = True ,
586626 transparent_trajectories = None ,
@@ -651,7 +691,8 @@ def _pose_mediapipe(
651691 avg_acc += frame
652692 avg_n += 1
653693
654- result = estimator .predict_frame (frame )
694+ with _suppress_native_stderr (quiet ):
695+ result = estimator .predict_frame (frame )
655696 keypoints = result .keypoints # shape (33, 3): x, y, visibility
656697
657698 # Collect data row: time + normalised (x, y) for every landmark.
@@ -703,7 +744,8 @@ def _pose_mediapipe(
703744 pb .progress (ii )
704745 ii += 1
705746
706- estimator .close ()
747+ with _suppress_native_stderr (quiet ):
748+ estimator .close ()
707749
708750 if save_video :
709751 video_out .stdin .close ()
0 commit comments