Hygiene patterns for any OpenCV + dlib vision pipeline: camera index probing + macOS init quirks, warmup that verifies real frames, frame-skip policy for expensive inference.
96
93%
Does it follow best practices?
Impact
100%
1.36xAverage score across 6 eval scenarios
Passed
No known issues
OpenCV's cv2.VideoCapture on macOS (and some Linux configs) is flaky in
specific, reproducible ways. This skill encodes the patterns that make camera
init deterministic.
isOpened() returns True but frames are black — camera is still
initialising internally. Reads return a zero frame (frame.mean() < 10).cap.read() after opening sometimes
returns a cached frame from a previous session, not a live one.import cv2, time, face_recognition
def open_camera(index: int) -> cv2.VideoCapture | None:
cap = cv2.VideoCapture(index)
if not cap.isOpened():
return None
# macOS needs 1s+ after open() before real frames arrive.
# 0.5s is NOT enough for USB webcams (DJI Osmo, Logitech) — they
# go dormant between processes and need a full re-handshake.
time.sleep(1.0)
# Read-until-non-black: some cameras return 5-20 dark frames before
# the sensor settles. Threshold mean > 30 (not 10 — dim rooms are
# 30-50; black frames are < 10).
for _ in range(30):
ok, frame = cap.read()
if ok and frame is not None and frame.mean() > 30:
return cap
time.sleep(0.1) # pace the probe — hammering cap.read() can stall
cap.release()
return NoneFor face-specific pipelines, take the probe further: only return once
face_recognition.face_locations(small_frame) succeeds at least once. That
guarantees the model is loaded AND the user is in frame before the timed loop
starts.
def probe_indices(max_index: int = 4):
for i in range(max_index):
cap = cv2.VideoCapture(i)
if not cap.isOpened():
cap.release()
continue
time.sleep(0.5)
best_mean = 0
res = None
for _ in range(10):
ok, f = cap.read()
if ok and f is not None and f.mean() > best_mean:
best_mean = f.mean()
res = f.shape[:2]
cap.release()
yield i, res, best_meanRule: pick the index where best_mean > 50 (real lighting) AND the
resolution matches a known physical camera. Skip any best_mean < 10 — those
are black-returning virtual cameras.
Cross-check with system_profiler SPCameraDataType on macOS to confirm which
physical camera you're talking to.
Some USB UVC cameras — confirmed on DJI Osmo Pocket 3 — return dark frames
(mean ≈ 25) even though isOpened() is True unless the macOS event loop is
pumped. OpenCV's cap.read() in a headless script will silently get zeros.
The fix: create a named window and call cv2.imshow + cv2.waitKey on each
frame. This pumps the NSApplication event loop that macOS needs to deliver
video buffers from the UVC driver.
cap = cv2.VideoCapture(index)
cv2.namedWindow('_cam', cv2.WINDOW_NORMAL)
time.sleep(1.0)
for _ in range(30):
ok, f = cap.read()
if ok and f is not None:
cv2.imshow('_cam', f)
cv2.waitKey(30)
if f.mean() > 30:
break
time.sleep(0.1)Do NOT call cap.set(CAP_PROP_FRAME_WIDTH/HEIGHT) on DJI Osmo. The
resolution request triggers a UVC re-negotiation that resets the Osmo's
digital zoom and exposure to defaults. Let the camera use whatever resolution
it was already set to. If you need a specific resolution, set it physically
on the Osmo's touchscreen before the script runs.
If your pipeline spawns sub-agents or worker processes that each open their
own VideoCapture, only ONE process at a time can hold the camera on macOS.
Others will get dark frames.
Pattern: have the parent process own the camera, capture frames to disk or shared memory, and let sub-agents read from there. Or run the full vision pipeline in the parent and only delegate non-camera work to subs.
Wire an "am I really seeing frames?" probe into the startup sequence and print wall-clock milestones. Invisible 5-second camera init while the audience watches a blank terminal is the worst possible way to open a talk.
scripts/camera_setup.py — open_camera(index), probe_indices(), and capture_to_disk(). Import directly instead of re-implementing.
frame-skip-policy — how to balance per-frame work against inference costface-recognition-calibration plugin: face-recognition-persistence — absorb detection dropouts that survive camera warmup