MNIST
Data Visualization
OpenCV
Matplotlib
Pyplot

Visualize MNIST dataset using OpenCV or Matplotlib/Pyplot

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The MNIST dataset contains 70,000 grayscale images of handwritten digits (28x28 pixels each). To visualize MNIST images, use matplotlib.pyplot.imshow() with cmap='gray' for quick plots, or cv2.imshow() with OpenCV for interactive windows. Matplotlib is better for grids, annotations, and notebook displays. OpenCV is better for real-time image processing pipelines. Both require the images to be reshaped from flat vectors (784,) to 2D arrays (28, 28) if loaded from certain sources.

Loading MNIST

python
1# Using TensorFlow/Keras
2from tensorflow.keras.datasets import mnist
3(X_train, y_train), (X_test, y_test) = mnist.load_data()
4print(X_train.shape)  # (60000, 28, 28)
5print(y_train.shape)  # (60000,)
6
7# Using PyTorch
8from torchvision import datasets
9train_data = datasets.MNIST(root='./data', train=True, download=True)
10image, label = train_data[0]  # PIL Image, int
11
12# Using scikit-learn (smaller subset — 1797 images, 8x8)
13from sklearn.datasets import load_digits
14digits = load_digits()
15print(digits.images.shape)  # (1797, 8, 8)

Visualize a Single Image with Matplotlib

python
1import matplotlib.pyplot as plt
2from tensorflow.keras.datasets import mnist
3
4(X_train, y_train), _ = mnist.load_data()
5
6plt.imshow(X_train[0], cmap='gray')
7plt.title(f"Label: {y_train[0]}")
8plt.axis('off')
9plt.show()

Visualize a Grid of Images

python
1import matplotlib.pyplot as plt
2import numpy as np
3from tensorflow.keras.datasets import mnist
4
5(X_train, y_train), _ = mnist.load_data()
6
7fig, axes = plt.subplots(4, 8, figsize=(12, 6))
8for i, ax in enumerate(axes.flat):
9    ax.imshow(X_train[i], cmap='gray')
10    ax.set_title(f"{y_train[i]}", fontsize=10)
11    ax.axis('off')
12plt.suptitle("MNIST Samples", fontsize=14)
13plt.tight_layout()
14plt.show()

Show One Example Per Digit

python
1import matplotlib.pyplot as plt
2import numpy as np
3from tensorflow.keras.datasets import mnist
4
5(X_train, y_train), _ = mnist.load_data()
6
7fig, axes = plt.subplots(2, 5, figsize=(12, 5))
8for digit in range(10):
9    ax = axes[digit // 5, digit % 5]
10    idx = np.where(y_train == digit)[0][0]
11    ax.imshow(X_train[idx], cmap='gray')
12    ax.set_title(f"Digit {digit}")
13    ax.axis('off')
14plt.tight_layout()
15plt.show()

Visualize with OpenCV

python
1import cv2
2import numpy as np
3from tensorflow.keras.datasets import mnist
4
5(X_train, y_train), _ = mnist.load_data()
6
7# Single image — resize for visibility
8img = X_train[0]
9resized = cv2.resize(img, (280, 280), interpolation=cv2.INTER_NEAREST)
10cv2.imshow(f"Digit: {y_train[0]}", resized)
11cv2.waitKey(0)
12cv2.destroyAllWindows()
13
14# Grid of images using OpenCV
15def make_grid(images, labels, rows=4, cols=8, img_size=56):
16    grid = np.zeros((rows * img_size, cols * img_size), dtype=np.uint8)
17    for i in range(rows * cols):
18        r, c = divmod(i, cols)
19        resized = cv2.resize(images[i], (img_size, img_size),
20                             interpolation=cv2.INTER_NEAREST)
21        grid[r*img_size:(r+1)*img_size, c*img_size:(c+1)*img_size] = resized
22        # Add label text
23        cv2.putText(grid, str(labels[i]),
24                     (c*img_size + 2, r*img_size + 12),
25                     cv2.FONT_HERSHEY_SIMPLEX, 0.4, 255, 1)
26    return grid
27
28grid = make_grid(X_train, y_train)
29cv2.imshow("MNIST Grid", grid)
30cv2.waitKey(0)
31cv2.destroyAllWindows()

Visualize Pixel Value Distribution

python
1import matplotlib.pyplot as plt
2import numpy as np
3from tensorflow.keras.datasets import mnist
4
5(X_train, y_train), _ = mnist.load_data()
6
7# Histogram of pixel values for digit 0 vs digit 1
8fig, axes = plt.subplots(1, 2, figsize=(12, 4))
9
10digit_0 = X_train[y_train == 0].flatten()
11digit_1 = X_train[y_train == 1].flatten()
12
13axes[0].hist(digit_0[digit_0 > 0], bins=50, alpha=0.7)
14axes[0].set_title("Pixel Distribution: Digit 0")
15axes[0].set_xlabel("Pixel Value")
16
17axes[1].hist(digit_1[digit_1 > 0], bins=50, alpha=0.7, color='orange')
18axes[1].set_title("Pixel Distribution: Digit 1")
19axes[1].set_xlabel("Pixel Value")
20
21plt.tight_layout()
22plt.show()
23
24# Average image per digit class
25fig, axes = plt.subplots(2, 5, figsize=(12, 5))
26for digit in range(10):
27    ax = axes[digit // 5, digit % 5]
28    avg_img = X_train[y_train == digit].mean(axis=0)
29    ax.imshow(avg_img, cmap='hot')
30    ax.set_title(f"Avg: {digit}")
31    ax.axis('off')
32plt.suptitle("Average Image Per Digit")
33plt.tight_layout()
34plt.show()

Handling Flat Vectors

python
1import matplotlib.pyplot as plt
2import numpy as np
3
4# Some loaders return flat (784,) vectors instead of (28, 28) images
5flat_data = np.random.randint(0, 256, size=(100, 784))  # Simulated flat MNIST
6
7# Reshape to 28x28 for visualization
8img = flat_data[0].reshape(28, 28)
9plt.imshow(img, cmap='gray')
10plt.show()
11
12# Batch reshape
13images_2d = flat_data.reshape(-1, 28, 28)
14print(images_2d.shape)  # (100, 28, 28)

Common Pitfalls

  • Forgetting cmap='gray' in matplotlib: Without it, matplotlib uses a color map (viridis by default) for grayscale images, making digits appear in blue/yellow instead of white/black. Always pass cmap='gray' to imshow() for grayscale data.
  • Not reshaping flat vectors to 28x28: Some sources (scikit-learn's fetch_openml, CSV files) return MNIST as flat 784-element vectors. Passing a flat vector to imshow() fails with a shape error. Reshape with img.reshape(28, 28) before displaying.
  • Pixel values in wrong range for OpenCV: OpenCV imshow expects uint8 (0-255) for grayscale display. If your images are normalized to 0.0-1.0 (common after preprocessing), multiply by 255 and cast: (img * 255).astype(np.uint8). Otherwise the display appears all black or all white.
  • OpenCV window not appearing or freezing: cv2.imshow() requires cv2.waitKey() to process window events. Without it, the window appears frozen or never renders. Use cv2.waitKey(0) to wait for a keypress or cv2.waitKey(1) in a loop for real-time display.
  • Mixing up TensorFlow and PyTorch image formats: TensorFlow MNIST returns (batch, 28, 28) with values 0-255. PyTorch transforms often produce (batch, 1, 28, 28) with values 0.0-1.0. Before visualizing PyTorch tensors, squeeze the channel dimension and convert: img.squeeze().numpy().

Summary

  • Use plt.imshow(img, cmap='gray') for quick MNIST visualization in notebooks and scripts
  • Use cv2.imshow() with cv2.resize() for interactive or real-time display
  • Create grids with plt.subplots() to compare multiple digits at once
  • Reshape flat 784-element vectors to (28, 28) before displaying
  • Normalize pixel values to the correct range for each library (0-255 for OpenCV, 0-1 or 0-255 for matplotlib)

Course illustration
Course illustration

All Rights Reserved.