Posting this because it cost me a release and the failure mode points nowhere
near the cause.
cv2.CascadeClassifier given a path that does not exist does NOT raise. You get
back an empty classifier, everything looks fine, and the error only appears
later, inside detectMultiScale:
(-215:Assertion failed) !empty() in function 'detectMultiScale'
Worse, it is invisible in development. It worked perfectly from a checkout and
broke every affected render in the installed build, because the frozen app
ships cv2 without its data directory, so the cascade XML simply is not there.
Same code, same version, different packaging.
Two things that fixed it:
-
Resolve the cascade path explicitly rather than relying on cv2’s data
directory being present, and treat a missing file as “no classifier” rather
than handing an empty one downstream. -
Build the cascades per thread. detectMultiScale is not thread-safe on a
shared instance, which is a separate trap if you are running detection
across parallel workers.
For context on where this bit me: I use YOLOv8 to find people, OpenCV Haar
cascades to find the face inside each person box, and TalkNet-ASD to work out
which of those faces is actually the one talking, so a 16:9 stream can be
cropped to 9:16 following the speaker rather than the biggest face. All local.
The cascade step is the fallback path, which is exactly why it went unnoticed
for a while.
The clip-selection side is a documented plug-in point if the pipeline is
useful to anyone: