Most OpenCV code starts in a notebook and never meets production. The library functions are easy; the system around them is where pipelines break.
Validate before you decode#
Customer uploads are hostile input. Check magic bytes, cap dimensions, reject animated formats you don't handle, and decode in a bounded memory budget. A 60MP image can eat hundreds of MB of RAM per request.
MAX_SIDE = 2048
def load_image(raw: bytes) -> "np.ndarray":
if len(raw) > MAX_BYTES:
raise PayloadTooLarge()
img = cv2.imdecode(np.frombuffer(raw, np.uint8), cv2.IMREAD_COLOR)
if img is None:
raise UnreadableImage()
return resize_bound(img, MAX_SIDE)Decode, then resize, before any expensive step. Everything downstream gets cheaper.
Color spaces are not optional#
OpenCV loads BGR. Models trained on RGB, matplotlib, and most export paths assume RGB. Silent channel swaps don't crash—they degrade accuracy by a few points, which makes them the worst kind of bug. Pick one convention at the boundary and convert explicitly.
Measure what production does#
Log input dimensions, processing time per stage, and failure categories. Real distributions are nothing like lab images: phone cameras, screenshots, dark rooms. Percentiles from production traffic will tell you which stage to optimize, while averages will lie to you.
Keep the pipeline pure#
Image functions should take arrays and return arrays, with side effects at the edges. Pure functions mean you can replay a failing input offline against the exact code that ran in production. That replay loop is what makes vision systems fixable.