figure sizing
image resolution
pixel dimensions
graphic design
data visualization

Specifying and saving a figure with exact size in pixels

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

Matplotlib specifies figure sizes in inches and DPI (dots per inch), not pixels directly. To save a figure with exact pixel dimensions, you need to set the figure size in inches and the DPI such that their product equals the desired pixel count. For example, an 800x600 pixel image at 100 DPI requires an 8x6 inch figure.

The Formula

 
width_inches = width_pixels / dpi
height_inches = height_pixels / dpi

For 800x600 pixels at 100 DPI:

  • Width: 800 / 100 = 8 inches
  • Height: 600 / 100 = 6 inches

Basic Approach

python
1import matplotlib.pyplot as plt
2import numpy as np
3
4# Desired pixel dimensions
5width_px, height_px = 800, 600
6dpi = 100
7
8fig, ax = plt.subplots(figsize=(width_px/dpi, height_px/dpi), dpi=dpi)
9
10x = np.linspace(0, 10, 100)
11ax.plot(x, np.sin(x))
12ax.set_title('Sine Wave')
13
14fig.savefig('plot.png', dpi=dpi)
15plt.close(fig)

The saved plot.png will be exactly 800x600 pixels.

Helper Function

Create a reusable function for pixel-exact figures:

python
1def figure_pixels(width_px, height_px, dpi=100):
2    """Create a matplotlib figure with exact pixel dimensions."""
3    return plt.subplots(
4        figsize=(width_px / dpi, height_px / dpi),
5        dpi=dpi
6    )
7
8# Usage
9fig, ax = figure_pixels(1920, 1080)
10ax.plot([1, 2, 3], [4, 5, 6])
11fig.savefig('hd_plot.png', dpi=fig.dpi)

Matching DPI on Save

The DPI must match between figure creation and saving. A mismatch produces the wrong pixel dimensions:

python
1# WRONG: DPI mismatch
2fig, ax = plt.subplots(figsize=(8, 6), dpi=100)  # 800x600 at creation
3fig.savefig('plot.png', dpi=150)  # saves as 1200x900!
4
5# CORRECT: use the figure's DPI
6fig.savefig('plot.png', dpi=fig.dpi)
7
8# Or specify both consistently
9dpi = 150
10fig, ax = plt.subplots(figsize=(800/dpi, 600/dpi), dpi=dpi)
11fig.savefig('plot.png', dpi=dpi)  # 800x600

Removing Padding and Margins

Matplotlib adds padding around the plot by default. To get exact pixel dimensions with no extra whitespace:

python
1fig, ax = figure_pixels(800, 600)
2ax.plot(x, np.sin(x))
3
4# Remove padding
5fig.savefig('plot.png', dpi=fig.dpi, bbox_inches='tight', pad_inches=0)

Note: bbox_inches='tight' adjusts the bounding box to fit content, which may change the actual pixel size. For strict pixel control, set margins manually instead:

python
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
fig.savefig('plot.png', dpi=fig.dpi)

High-DPI (Retina) Figures

For Retina displays, use higher DPI:

python
1# 800x600 logical pixels, 1600x1200 actual pixels (2x Retina)
2dpi = 200
3fig, ax = plt.subplots(figsize=(800/dpi, 600/dpi), dpi=dpi)
4ax.plot(x, np.sin(x))
5fig.savefig('retina_plot.png', dpi=dpi)

Working with Different File Formats

Different formats handle DPI settings differently:

python
1fig, ax = figure_pixels(800, 600, dpi=100)
2ax.plot(x, np.sin(x))
3
4# PNG: pixel-perfect, uses DPI metadata
5fig.savefig('plot.png', dpi=fig.dpi)
6
7# SVG: vector format, DPI affects rasterized elements only
8fig.savefig('plot.svg')
9
10# PDF: vector format with DPI for rasterized elements
11fig.savefig('plot.pdf', dpi=fig.dpi)
12
13# JPEG: supports DPI, uses lossy compression
14fig.savefig('plot.jpg', dpi=fig.dpi, quality=95)

Verifying Pixel Dimensions

python
1from PIL import Image
2
3img = Image.open('plot.png')
4print(f"Size: {img.size}")        # (800, 600)
5print(f"DPI: {img.info.get('dpi')}")  # (100.0, 100.0)

Or from the command line:

bash
1# Using ImageMagick
2identify plot.png
3# plot.png PNG 800x600 800x600+0+0 8-bit sRGB
4
5# Using file command
6file plot.png

Multiple Subplots with Exact Size

python
1width_px, height_px = 1200, 800
2dpi = 100
3
4fig, axes = plt.subplots(2, 2, figsize=(width_px/dpi, height_px/dpi), dpi=dpi)
5
6for ax in axes.flat:
7    ax.plot(np.random.randn(50))
8
9fig.tight_layout()
10fig.savefig('subplots.png', dpi=dpi)
11# Result: exactly 1200x800 pixels

Common Pitfalls

  • DPI Matching: Always ensure that the DPI specified during figure creation is matched when saving the figure. A mismatch can lead to incorrect pixel dimensions.
  • Aspect Ratio: Check that altering dimensions does not unintentionally distort your graphical elements. Use ax.set_aspect('equal') for square axes.
  • File Formats: Different formats (PNG, TIFF, etc.) may handle scaling and DPI settings differently. PNG is generally recommended for maintaining pixel accuracy.
  • bbox_inches='tight': This option recalculates the bounding box based on content, which overrides your specified figure size. Avoid it when exact pixel dimensions matter.
  • Interactive display vs save: plt.show() may render at screen DPI, which differs from your save DPI. The saved file has the correct dimensions; the interactive display may differ.
  • Backend differences: Some Matplotlib backends (Agg, Cairo) may produce slightly different results. The Agg backend is the default and most reliable for PNG output.

Summary

  • Set figure size in inches = desired pixels / DPI: figsize=(px/dpi, py/dpi)
  • Always use the same DPI for creation and saving: fig.savefig('out.png', dpi=fig.dpi)
  • Use PNG format for pixel-accurate output
  • Avoid bbox_inches='tight' when exact dimensions are critical
  • Verify output dimensions with PIL or ImageMagick

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.