Reading, writing and showing images

OpenCV · Essentials · 8 min

Getting pixels in and out reliably is the first step of every vision script, and imread fails silently.

cv2.imread(path, flags) loads an image file into a NumPy array. The flag picks what you get:

  • cv2.IMREAD_COLOR (default): 3-channel BGR, any alpha dropped
  • cv2.IMREAD_GRAYSCALE: single channel
  • cv2.IMREAD_UNCHANGED: keep alpha and bit depth, e.g. 16-bit PNGs

The big gotcha: imread does not raise on a missing or unreadable file. It returns None, and the error only surfaces later as a confusing 'NoneType' object has no attribute 'shape'. Always check the result.

cv2.imwrite(path, img, params) picks the format from the file extension and returns True on success. Optional params control encoding, for example [cv2.IMWRITE_JPEG_QUALITY, 90] or [cv2.IMWRITE_PNG_COMPRESSION, 9].

To work in memory (web uploads, databases), use cv2.imencode('.png', img), which returns (ok, buffer), and cv2.imdecode(np_bytes, flags). This is also the workaround for file paths with non-ASCII characters on Windows.

For a quick look, cv2.imshow(window_name, img) opens a window, but nothing is drawn until you call cv2.waitKey(ms), which also pumps the GUI events; waitKey(0) waits for a key. Finish with cv2.destroyAllWindows(). In Jupyter or on headless servers (opencv-python-headless), there is no GUI: convert to RGB and show with matplotlib instead.

Example

import cv2
import numpy as np

img = np.full((60, 80, 3), (0, 128, 255), np.uint8)    # orange in BGR
ok = cv2.imwrite("orange.png", img)
print(ok)                                         # True

back = cv2.imread("orange.png")                   # BGR uint8
print(back.shape, back[0, 0])                     # (60, 80, 3) [  0 128 255]
gray = cv2.imread("orange.png", cv2.IMREAD_GRAYSCALE)
print(gray.shape)                                 # (60, 80)

missing = cv2.imread("no_such_file.jpg")
print(missing is None)                            # True -- no exception!

ok, buf = cv2.imencode(".jpg", img, [cv2.IMWRITE_JPEG_QUALITY, 90])
print(ok, buf.dtype)                              # True uint8 (encoded bytes)
decoded = cv2.imdecode(buf, cv2.IMREAD_COLOR)
print(decoded.shape)                              # (60, 80, 3)

Remember

  • imread returns None instead of raising: always check it
  • The flag chooses colour, grayscale or unchanged (alpha, 16-bit)
  • imwrite picks the format from the extension; imencode/imdecode work in memory
  • imshow needs waitKey to actually draw

Watch out for

  • Calling cv2.imshow without cv2.waitKey, so the window never paints, or using it on a headless install.

Check yourself

  1. What does `cv2.imread('typo.jpg')` do if the file does not exist?
  2. You need to keep a PNG's transparency when loading it. Which flag do you use?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.opencv.org/4.x/db/deb/tutorial_display_image.html