Python · Computer Vision · Quick Reference

cv2 · OpenCV 4.x / 5.0

The whole library rests on one idea: an image is a NumPy array of shape (H, W, 3), uint8, in B·G·R order — and almost every function reads one array and returns another. Learn the array, and the 2,500 functions become variations on a theme. Colour-coded by domain; marks the ones you'll reach for daily.

setup · I/O array · colour · draw transform · filter threshold · morph · edges contours · features · detect video · dnn · interop gotcha most common
Verified against a live cv2 4.13.0 + numpy 2.4.4 install (every gotcha run, not remembered) · docs.opencv.org 4.13 · OpenCV-Python tutorials · LearnOpenCV · PyImageSearch · GeeksforGeeks. Re-verified 2026-08-28: current 4.x is 4.14; OpenCV 5.0 shipped Jul 2026 (major — rewritten DNN engine; the cv2 API here is stable across 4.x/5.0).
THE PIPELINE — READ · PROCESS · ANALYZE · SHOW Read imread() VideoCapture() the ndarray (H, W, 3) uint8 · BGR every step reads & returns this Process cvtColor·blur threshold·morph resize·warp Analyze findContours·ORB matchTemplate detectMultiScale Show / Save imshow+waitKey imwrite VideoWriter AN IMAGE IS A NumPy ARRAY Indexing, colour order and size tuples all follow from this one fact. the grid · origin top-left x → width (cols) y ↓ height (rows) three channel planes R G B img[:,:,0] stored B · G · R point (x, y) ≠ index [y, x] circle(img,(x,y)) · img[y, x] size tuple (W, H) ≠ shape (H,W,3) resize(img,(w,h)) · img.shape BGR, never RGB (255,0,0) is BLUE, not red the 10-second quickstart import cv2 img = cv2.imread('in.png') g = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) e = cv2.Canny(g, 100, 200) cv2.imwrite('out.png', e) read → grayscale → edges → save.
01

Setup & import

  • $ pip install opencv-python — the main build: core + GUI (imshow).
  • $ pip install opencv-contrib-python — adds extra/patented modules (SIFT lives in core now, but tracking, aruco, etc. are here).
  • $ pip install opencv-python-headless — no GUI; for servers / Docker / notebooks.
  • import cv2 — the import name is cv2, not opencv.
  • import numpy as np — you'll need it; images are numpy arrays.
  • cv2.__version__ — check the build; '4.13.0' here. Latest 4.x is '4.14.x'; opencv-python==5.0.0.x installs OpenCV 5.0.
  • OpenCV 5.0 (Jul 2026): the legacy C API is gone (C++17 core), and the ml module + G-API moved to opencv-contrib-python — the Python cv2 surface below is otherwise unchanged.
  • Pick one of the three packages — installing several fights over the same cv2 namespace.
02

Read · write · display

  • img = cv2.imread('x.png') — returns an (H,W,3) BGR array, or None on failure.
  • cv2.imread(p, cv2.IMREAD_GRAYSCALE) — load as 1-channel (H,W). Flags: IMREAD_COLOR=1, GRAYSCALE=0, UNCHANGED=-1 (keeps alpha).
  • if img is None: raise ... always guard: a wrong path fails silently, no exception.
  • cv2.imwrite('out.jpg', img) — format inferred from extension; returns True/False.
  • cv2.imshow('win', img) — opens a window (needs a GUI build).
  • cv2.waitKey(0) mandatory after imshow; 0=wait forever, ms=timeout. Returns key code.
  • cv2.destroyAllWindows() — close every window.
  • cv2.imdecode/imencode — go to/from raw byte buffers (HTTP, sockets, in-memory).
03

The image array

  • img.shape (H, W, 3) colour, or (H, W) grey. Height first!
  • img.dtype → usually uint8 (0–255). Also .size, .ndim.
  • px = img[y, x] — index is [row, col] = [y, x]: the opposite of the (x,y) you pass to drawing fns.
  • roi = img[y1:y2, x1:x2] — crop = plain numpy slice. It's a view; .copy() to detach.
  • b, g, r = cv2.split(img) — separate channels (slow); cv2.merge([b,g,r]) to rejoin.
  • b = img[:, :, 0] — faster than split for one channel (index 0 = Blue).
  • canvas = np.zeros((h,w,3), np.uint8) — a black image to draw on.
  • img[y1:y2, x1:x2] = 0 — assign into a region to blank/paint it.
04

Colour spaces

  • g = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) — the workhorse; most analysis wants grayscale.
  • cv2.cvtColor(img, cv2.COLOR_BGR2HSV) — for colour segmentation: Hue is stable under lighting.
  • cv2.cvtColor(img, cv2.COLOR_BGR2RGB) — before handing to matplotlib / PIL / most ML.
  • mask = cv2.inRange(hsv, lo, hi) — binary mask where pixels fall in a colour band. Core of "find the red ball".
  • # HSV: H 0–179, S 0–255, V 0–255 Hue is halved to fit uint8 (360°→180). Red wraps at both ends.
  • Grayscale drops colour but keeps shape — nearly every detector, threshold and edge op needs it.
05

Drawing & text

  • cv2.rectangle(img, pt1, pt2, color, t) — corners as (x,y); colour is a BGR tuple.
  • cv2.line(img, pt1, pt2, color, t)
  • cv2.circle(img, center, radius, color, t) center is (x, y).
  • cv2.putText(img, 'hi', org, font, scale, color, t) org is the bottom-left corner; use FONT_HERSHEY_SIMPLEX.
  • cv2.polylines(img, [pts], isClosed, ...) pts as an int32 array of shape (N,1,2).
  • thickness=-1 — fills the shape solid (rect/circle/ellipse).
  • All drawing mutates in place and returns nothing useful — draw on a .copy() to keep the original.
06

Resize & geometry

  • cv2.resize(img, (w, h)) — dsize is (width, height) — the reverse of .shape. The #1 tuple-order trap.
  • cv2.resize(img, None, fx=.5, fy=.5) — scale by factor instead of target size.
  • interpolation=cv2.INTER_AREA — best for shrinking; INTER_CUBIC/LINEAR for enlarging.
  • cv2.flip(img, 1) 1=horizontal, 0=vertical, -1=both.
  • cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) — exact 90/180 turns, no matrix needed.
  • M = cv2.getRotationMatrix2D(c, ang, s) — build a 2×3 affine matrix for arbitrary angle.
  • cv2.warpAffine(img, M, (w,h)) — apply rotation / translation / shear.
  • cv2.warpPerspective(img, M, size) — 3×3 homography from getPerspectiveTransform; "bird's-eye" un-warp.
07

Blur & smooth

  • cv2.GaussianBlur(img, (5,5), 0) — the default denoiser; ksize must be odd. Sigma 0 = derive from ksize.
  • cv2.medianBlur(img, 5) — kills salt-and-pepper noise; preserves edges.
  • cv2.blur(img, (5,5)) — plain box/average blur.
  • cv2.bilateralFilter(img, 9, 75, 75) — smooths while keeping edges sharp (slow but pretty).
  • cv2.filter2D(img, -1, kernel) — apply any custom convolution kernel (sharpen, emboss…).
  • Blur before thresholding/Canny to suppress noise-driven false edges.
08

Arithmetic & bitwise

  • cv2.add(a, b) saturates: 250+10 → 255. Plain a+b (numpy) wraps: 250+10 → 4.
  • cv2.addWeighted(a, .7, b, .3, 0) — blend two images: α·a + β·b + γ.
  • cv2.subtract(a, b) — saturating subtract (clamps at 0).
  • cv2.bitwise_and(img, img, mask=m) — keep only masked region; core of compositing/segmentation.
  • cv2.bitwise_or / _not / _xor — combine/invert masks.
  • Reach for cv2.add over + whenever overflow matters — the wrap-around bug is silent.
09

Thresholding

  • ret, dst = cv2.threshold(g, 127, 255, cv2.THRESH_BINARY) — returns a tuple — unpack it. Input must be grayscale.
  • cv2.THRESH_BINARY_INV · TRUNC · TOZERO — invert, clip-to-T, or zero-below-T variants.
  • cv2.THRESH_BINARY + cv2.THRESH_OTSU — pass thresh=0; Otsu picks T automatically from the histogram.
  • cv2.adaptiveThreshold(g, 255, ...) — per-region T; wins under uneven lighting (scanned pages).
  • The gateway to contours & morphology — most binary pipelines start here.
10

Morphology

  • k = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)) — the "brush". Also MORPH_RECT, MORPH_CROSS.
  • cv2.erode(img, k) — shrinks white regions (thins).
  • cv2.dilate(img, k) — grows white regions (thickens, joins gaps).
  • cv2.morphologyEx(img, cv2.MORPH_OPEN, k) Open = erode→dilate: removes small specks.
  • cv2.morphologyEx(img, cv2.MORPH_CLOSE, k) Close = dilate→erode: fills small holes.
  • cv2.MORPH_GRADIENT · TOPHAT · BLACKHAT — outline, bright-spots, dark-spots.
  • Add iterations=n to apply repeatedly. Works on binary/grayscale images.
11

Gradients & edges

  • edges = cv2.Canny(g, 100, 200) — the go-to edge detector. Keep low:high near 1:2–1:3. Blur first.
  • cv2.Sobel(g, cv2.CV_64F, 1, 0) — x-derivative (vertical edges); (0,1) for y.
  • cv2.Laplacian(g, cv2.CV_64F) — 2nd derivative; blob/edge response in one pass.
  • ddepth = cv2.CV_64F — gradients go negative; using uint8 silently clips them to 0. Compute in float, then np.absoluteuint8.
  • Canny runs on a single-channel grayscale image, not colour.
12

Contours

  • cnts, hier = cv2.findContours(bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) — returns 2 values in 4.x (3.x returned 3 — the classic version bug).
  • cv2.RETR_EXTERNAL — outermost only; RETR_TREE keeps the full nesting hierarchy.
  • cv2.CHAIN_APPROX_SIMPLE — compress straight runs to endpoints (4 points for a rectangle, not hundreds).
  • cv2.drawContours(img, cnts, -1, color, t) -1 = draw all; or pass a single index.
  • cv2.contourArea(c) · cv2.arcLength(c, True) — area & perimeter; filter tiny noise contours by area.
  • Feed it a binary image (threshold/Canny output). It modifies input in old versions — pass a copy if unsure.
13

Shape analysis

  • x, y, w, h = cv2.boundingRect(c) — upright bounding box; the everyday "put a box around it".
  • cv2.minAreaRect(c) — rotated box ((cx,cy),(w,h),angle); use cv2.boxPoints to draw.
  • cv2.minEnclosingCircle(c) (center), radius.
  • cv2.convexHull(c) — tightest convex wrap around the points.
  • approx = cv2.approxPolyDP(c, ε, True) — simplify to N vertices; len(approx) counts sides (3=triangle, 4=quad…).
  • M = cv2.moments(c) — centroid: cx = M['m10']/M['m00'].
14

Histograms

  • h = cv2.calcHist([img], [0], None, [256], [0,256]) — note everything is wrapped in lists.
  • cv2.equalizeHist(g) — stretch contrast globally (grayscale only).
  • clahe = cv2.createCLAHE(clipLimit=2.)
    clahe.apply(g) adaptive local equalization; avoids blowing out bright regions.
  • cv2.normalize(x, None, 0, 255, cv2.NORM_MINMAX) — rescale values into a range.
15

Features & corners

  • orb = cv2.ORB_create(nfeatures)
    kp, des = orb.detectAndCompute(g, None) — fast, free, patent-free keypoints + binary descriptors.
  • sift = cv2.SIFT_create() — now in core & free (since 4.4); more robust, float descriptors.
  • cv2.goodFeaturesToTrack(g, n, q, d) — Shi-Tomasi corners; classic for optical-flow tracking.
  • cv2.cornerHarris(g, 2, 3, .04) — Harris corner response map.
  • cv2.drawKeypoints(img, kp, None) — visualize detected keypoints.
16

Matching & template

  • bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) NORM_HAMMING for ORB/BRIEF; NORM_L2 for SIFT/SURF.
  • m = sorted(bf.match(d1, d2), key=lambda x: x.distance) — best matches have the smallest distance.
  • bf.knnMatch(d1, d2, k=2) — then Lowe's ratio test: keep m if m.distance < 0.75·n.distance.
  • cv2.FlannBasedMatcher(...) — approximate but fast for large descriptor sets.
  • res = cv2.matchTemplate(img, tmpl, cv2.TM_CCOEFF_NORMED)
    _,_,_, loc = cv2.minMaxLoc(res) — slide a patch; peak = best match location.
  • Template matching is rigid — no scale/rotation tolerance. Use feature matching when the object changes size or angle.
17

Video I/O

  • cap = cv2.VideoCapture(0) 0 = default webcam; or a file path / stream URL.
  • ret, frame = cap.read() check ret: False at end-of-stream or camera error.
  • while cap.isOpened(): ret, frame = cap.read() ... — the standard frame loop; break on not ret.
  • cap.get(cv2.CAP_PROP_FPS) — also FRAME_WIDTH, FRAME_HEIGHT, FRAME_COUNT.
  • fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter('o.mp4', fourcc, 30, (w,h)) — writer frame size must match the frames, or you get an empty file.
  • cap.release() · out.release() — always release, or the device/file stays locked.
18

Detection & DNN

  • fc = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') — pretrained XMLs ship with cv2.
  • boxes = fc.detectMultiScale(g, 1.1, 4) → list of (x, y, w, h). Args: scaleFactor, minNeighbors.
  • hog = cv2.HOGDescriptor() — classic person detector (setSVMDetector + detectMultiScale).
  • net = cv2.dnn.readNet(weights, config) — run YOLO/Caffe/ONNX models on the CPU with no framework.
  • blob = cv2.dnn.blobFromImage(img, ...) — preprocess into the 4-D tensor the net expects; net.setInputnet.forward().
  • OpenCV 5.0 ships a rewritten DNN engine (runs alongside the classic one): dynamic shapes, subgraphs & modern ONNX — now >80% of the ONNX spec (was <23% in 4.x), plus LLM/VLM support. readNet/blobFromImage stay the same.
19

Interop & display

  • plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) — matplotlib expects RGB; skip the convert and colours look wrong.
  • # in notebooks, cv2.imshow hangs — use matplotlib, or cv2_imshow in Colab.
  • img = np.clip(x, 0, 255).astype(np.uint8) — cast float results back to displayable uint8.
  • cv2.copyMakeBorder(img, t,b,l,r, cv2.BORDER_CONSTANT) — pad an image (letterbox / kernel margins).
  • Because img is a plain ndarray, all of numpy, scikit-image, Pillow and torch tensors interoperate with a cast/convert.

Flags you'll reuse

  • IMREAD_ COLOR=1 · GRAYSCALE=0 · UNCHANGED=-1
  • COLOR_BGR2 GRAY=6 · RGB=4 · HSV=40
  • THRESH_ BINARY=0 · _INV=1 · TRUNC=2 · TOZERO=3 · OTSU=8 (add it)
  • INTER_ NEAREST=0 · LINEAR=1 · CUBIC=2 · AREA=3 · LANCZOS4=4
  • MORPH_ RECT=0 · ELLIPSE=2 · OPEN=2 · CLOSE=3 · GRADIENT=4
  • RETR_ EXTERNAL=0 · LIST=1 · TREE=3
  • BORDER_ CONSTANT=0 · REPLICATE=1 · REFLECT=2
  • ddepth CV_8U=0 (images) · CV_64F=6 (gradients)
  • NORM_ L2=4 (SIFT) · HAMMING=6 (ORB)
  • FONT_HERSHEY_SIMPLEX =0 — the default putText font

Four ideas that explain the rest

1 · An image is an (H, W, 3) array

One pixel is three bytes, ordered B·G·R — never R·G·B.

x → cols (W) y ↓ rows (H) img[1, 2] B G R 0 1 2 order is B · G · R (255,0,0) = pure blue

2 · Convolution = a sliding kernel

Blur, sharpen, Sobel & Laplacian are all one small window ⊛ a kernel.

input pixels kernel 121 242 121 = Σ one output px = Σ (kernel × overlapping pixels), for every position a blur kernel averages · a Sobel kernel differences

3 · What each threshold type does

Intensity in → intensity out, split at level T.

BINARY T BINARY_INV T TRUNC T TOZERO T x-axis: input intensity y-axis: output intensity OTSU picks T for you from the image histogram.

4 · Erode shrinks · dilate grows

Open removes a speck; close fills a hole.

erode original dilate OPEN → de-speckle CLOSE → fill holes

Worth memorizing

The dozen facts behind most OpenCV bugs — all confirmed on a live cv2 4.13.0 install.
01
BGR, not RGB. (255,0,0) is blue. Convert before matplotlib/PIL/ML.
02
imread fails silently → returns None, no exception. Always guard it.
03
shape is (H, W, C) — height first, channels last.
04
Point (x, y) ≠ index [y, x]. Drawing takes (x,y); numpy takes [row, col].
05
resize wants (w, h) — the reverse of .shape. The most common tuple flip.
06
waitKey is mandatory after imshow, or the window never paints.
07
cv2.add saturates (250+10→255); numpy + wraps (→4).
08
Canny low:high ≈ 1:2–1:3, e.g. (100, 200). Blur first.
09
Hue is 0–179 (360° halved for uint8), not 0–359.
10
Kernel sizes must be odd(5,5), (3,3); even sizes error or shift.
11
Gradients need CV_64F. uint8 clips negative slopes to 0. Compute in float, then cast.
12
findContours → 2 values in 4.x (3.x returned 3). threshold → (ret, dst).