Reading, writing and inspecting images

Load an image into a NumPy array, understand its shape and colour order, crop regions, and save results without losing data.

Load and save

An OpenCV image is a NumPy array - height first, then width, then channels. Reading returns None on failure instead of raising, so the first thing every script should do is check for it.

import cv2

img = cv2.imread("photo.jpg")                 # BGR order, dtype uint8
if img is None:
    raise SystemExit("could not read photo.jpg - missing file, wrong path or unsupported format")

print(img.shape, img.dtype)                   # (1080, 1920, 3) uint8

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # shape (1080, 1920)
ok = cv2.imwrite("out/gray.png", gray)        # returns True/False, never raises
print(ok)
CallReturnsNotes
imread(path)Array or NoneAlways check for None; OpenCV reports errors by return value
imread(path, IMREAD_COLOR)3-channel BGRDrops alpha; the default
imread(path, IMREAD_GRAYSCALE)Single channelFastest path for most classical CV work
imread(path, IMREAD_UNCHANGED)As storedKeeps alpha and 16-bit depth; use for measurement
imwrite(path, img)boolExtension decides the format; a bad path returns False
💡
OpenCV loads colour images as BGR, while PIL, matplotlib and most browser APIs use RGB. If reds and blues look swapped in a preview, you forgot a conversion - the classic one-liner is cv2.cvtColor(img, cv2.COLOR_BGR2RGB).

Indexing, colour and resizing

# Slicing is (y, x) - rows first, then columns
crop = img[100:250, 300:500]                 # region of interest, a view not a copy

cv2.rectangle(img, (300, 100), (500, 250), (0, 255, 0), 2)   # (x1, y1), (x2, y2)
cv2.putText(img, "plate", (300, 92), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)

small = cv2.resize(img, (640, 360), interpolation=cv2.INTER_AREA)   # shrink
big = cv2.resize(small, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)   # enlarge
  • Use INTER_AREA when shrinking - it averages pixels and avoids aliasing.
  • Use INTER_CUBIC or INTER_LINEAR when enlarging; nearest-neighbour gives blocky edges.
  • Slicing returns a view, so writing into a crop also writes into the original. Call .copy() when you intend to modify one independently.
  • Resize is a coordinate change: bounding boxes and calibration values computed for the old size no longer apply.

FAQ

Why does my matplotlib preview look colour-shifted?
matplotlib assumes RGB and OpenCV gives BGR. Convert with cv2.cvtColor(img, cv2.COLOR_BGR2RGB) before plotting, or display with cv2.imshow.
cv2.imread returns None for a file I can open.
Check the working directory first - a relative path resolves from wherever the process started, not from the script. Then check the extension is supported and that the path has no characters your build cannot encode.

Filters and edge detection Opening, inspecting and saving images

Last refreshed 2026-09-18.