Skip to content

Image compression

Cut an image into 8 × 8 blocks, take each block's DCT, round it with JPEG's table, and watch quality trade bits against error.

Before thisThe discrete cosine transform (13.6), The 2-D DFT (28.3)

3 more before it

How big is a signal (1.3), Quantization & noise (11.1), Image filtering (28.2)

Before this13.6 · 28.3 · 3 more
Chapter 28 · Lesson 4 of 4

First, the picture

JPEG cuts an image into blocks of 8 by 8 pixels and describes each with as few numbers as it can. Watch two blocks below: the smooth one keeps 2 numbers out of 64, the one across an edge 27.

Smooth blocks need few numbers

Two 8 × 8 blocks of the test card at quality 50: one on the gradient, one across the disc's edge.

A smooth block from the gradient: values 131 to 136.

block
smooth
nonzero after rounding
—
energy in the first 10 zig-zag numbers
—
0.00 / 16.00 s
Describe this picture

Two 8 × 8 blocks of the test card at quality 50: one on the gradient, one across the disc’s edge. A small picture of the card outlines the two blocks, a dashed square for the smooth one and a solid square for the edge one, each labelled. Then come three 8 × 8 grids. The first shows the pixels’ grey levels, 0 to 255, with each value printed. The second shades each cell of the DCT by log⁡10∣XDCT∣\log_{10}\lvert X_\text{DCT}\rvert, from −1 to 3, and marks its sign with a small + or −. The third prints the quantised whole numbers and leaves the zeros blank. Under the grids is the zig-zag sequence, up to its last nonzero number. The readouts are the block, the numbers left nonzero after rounding, out of 64, and the energy in the first 10 zig-zag numbers, in percent. There is no control. The 16 s clip opens on the smooth block, values 131 to 136; its DCT, quantised grid and sequence fill in. Its DCT puts 99.8 % of the energy in the first ten zig-zag places, and after rounding only 2 numbers are left: 3, −1. From 8 s the edge block replaces it, values 97 to 230: 89.3 % in the first ten places, and 27 numbers survive, the last at place 59.

Smooth blocks need few numbers

A grey image of 128 by 128 pixels, one byte per pixel, takes 16 384 bytes. By this page’s estimate, JPEG’s standard setting needs about a tenth of that. Where do the savings come from?

The idea is one you met in The discrete cosine transform (13.6), in “Keep a few numbers, rebuild”. A smooth stretch of signal needs only a few DCT numbers. An image is mostly smooth stretches, with edges here and there. So JPEG spends its numbers where the edges are.

This page builds JPEG’s core in four steps: cut the image into blocks, take each block’s DCT, round the DCT numbers, and read them in a clever order. Real JPEG also handles colour; here every image is grey.

The test card on JPEG’s scale

I use the test card of Image filtering (28.2), from “What the weights add up to”. It is 128 by 128 pixels, with x[n1,n2]x[n_1,n_2] the pixel in column n1n_1 and row n2n_2, as in Images as signals (28.1).

Its values run from 0 to 1. There is a gradient that brightens across, 0.25+0.35 n1/1270.25+0.35\,n_1/127; a bright disc of 0.9; a dark square of 0.1; and a patch of diagonal stripes.

JPEG stores a grey pixel as a whole number from 0 to 255, so I multiply each value by 255 and round. On that scale the card runs from 26 to 230.

Then JPEG subtracts 128 from every pixel, so mid-grey becomes 0 and the values run from −102 to 102. This is the level shift.

Blocks of 8 by 8

JPEG does not transform the whole image at once. It cuts it into blocks of 8 by 8 pixels and treats each block on its own. The card has 16 blocks across and 16 down, 256 in all.

Why small blocks? A large transform would mix the sky at the top of a photo with the grass at the bottom. Inside a small block, the picture is usually either smooth or crossed by one or two edges.

Inside a block I count columns and rows from 0 to 7 again, and keep the names n1n_1 and n2n_2.

The DCT of a block

In “Rows first, then columns” of The 2-D DFT (28.3), a 2-D transform was a 1-D transform of every row, then of every column. JPEG does the same with 13.6’s orthonormal DCT: an 8-point DCT of each row of the block, then of each column of the result.

Written out, both passes together give

XDCT[k1,k2]=wk1wk2×∑n1=07∑n2=07[(x[n1,n2]−128)×cos⁡πk1(2n1+1)16×cos⁡πk2(2n2+1)16],\begin{aligned} &X_\text{DCT}[k_1,k_2]=w_{k_1}w_{k_2}\\ &\quad\times\sum_{n_1=0}^{7}\sum_{n_2=0}^{7}\Big[\big(x[n_1,n_2]-128\big)\\ &\qquad\times\cos\frac{\pi k_1(2n_1+1)}{16}\\ &\qquad\times\cos\frac{\pi k_2(2n_2+1)}{16}\Big], \end{aligned}

with 13.6’s weights for N=8N=8: w0=1/8w_0=\sqrt{1/8} and wk=2/8=12w_k=\sqrt{2/8}=\tfrac12 for k≥1k\ge1. Here k1k_1 counts cosine half-periods across the block and k2k_2 counts them down.

Each pair (k1,k2)(k_1,k_2) belongs to one little cosine pattern of 8 by 8 pixels. XDCT[0,0]X_\text{DCT}[0,0] is the flat pattern; its number is 8 times the block’s mean after the shift. Larger k1k_1 means finer stripes across, and larger k2k_2 finer stripes down.

13.6 showed that the orthonormal DCT keeps the energy. The same holds here, in both passes, so the 64 squared DCT numbers add up to the 64 squared pixels (after the shift).

Rounding with a table

Now the step that saves bits and loses detail. Each DCT number is divided by its own step Δ[k1,k2]\Delta[k_1,k_2] and rounded, as in “Rounding to the nearest level” of Quantization & noise (11.1). The file keeps only the whole number

stored=round ⁣(XDCT[k1,k2]Δ[k1,k2]),\text{stored}=\mathrm{round}\!\left(\frac{X_\text{DCT}[k_1,k_2]}{\Delta[k_1,k_2]}\right),

and the decoder later multiplies it back by Δ[k1,k2]\Delta[k_1,k_2]. As in 11.1, the rounding moves each DCT number by at most half its step.

The 64 steps form the quantisation table. The JPEG standard suggests this one for brightness, in its Annex K. Each row is one k2k_2, from 0 at the top, and each column one k1k_1, from 0 at the left:

16111016244051611212141926586055141316244057695614172229518780621822375668109103772435556481104113924964788710312112010172929598112100103990011223344556677k₁ acrossk₂ down
Fig. The luminance table of Annex K, JPEG’s quality 50. The stronger a cell’s shading, the coarser its step.

The steps are small in the top left corner, from 10 to 16, and large towards the bottom right, up to 121. Fine patterns get coarse steps, because the eye notices errors in them less. Most fine-pattern numbers are small anyway, so a coarse step rounds them to 0.

The table is JPEG’s quality 50. The second instrument shows how other qualities scale it.

Reading in zig-zag order

The rounded block is 64 whole numbers, and most of the large ones sit near the top left. JPEG reads them in zig-zag order: start at the corner, step across, then sweep back and forth along the diagonals to the far corner.

k₁ acrossk₂ down1234567891064
Fig. Zig-zag order. Place 1 is the flat pattern, place 2 one step across, place 3 one step down; places 1 to 10 fill the four diagonals nearest the corner.

The order runs from coarse patterns to fine ones. So a typical block’s sequence starts with a few nonzero numbers and ends in a long run of zeros. A coder does not write those zeros one by one. It writes “the rest are zero”, which costs almost nothing.

Two blocks of the card

Let’s try it on two blocks of the card, both at quality 50. The first is smooth: columns 96 to 103 and rows 8 to 15, on the gradient. The second crosses the disc’s edge: columns 48 to 55 and rows 16 to 23.

Think of describing a blank wall: one sentence does it. Describing a doorway in that wall takes several. The picture at the top of the page shows both blocks; watch how many numbers each one keeps.

Notice the smooth block’s sequence: 3, −1, then 62 zeros. Two numbers describe 64 pixels, to within a level.

Why the smooth block packs

Look at the smooth block’s pixels. Every row is the same: 131, 132, 133, 133, 134, 135, 135, 136. The gradient changes only across, so down each column the value does not change at all.

The two passes can run in either order, so take the columns first. A column with one value is flat, so its DCT down the column keeps only k2=0k_2=0. Every DCT number with k2≥1k_2\ge1 is 0, and only the top row of the DCT grid survives.

That top row is 13.6’s story again: the DCT of one smooth row. The flat number is 45.0, the first stripe across is −12.31, and the other six are 1.50 or less in size.

The flat number alone holds 92.7 % of the energy, and with the first stripe 99.7 %.

Rounding finishes the job. 45.0/16=2.845.0/16=2.8 rounds to 3, and −12.31/11=−1.12-12.31/11=-1.12 rounds to −1. The six small numbers, divided by steps from 10 to 61, all land within 0.15 of 0 and round to 0.

The decoder multiplies back: 3 × 16 = 48 and −1 × 11 = −11, then runs the inverse DCT. Rounded to whole levels, every rebuilt pixel is within 1 level of the original, with an RMS error of 0.61 levels.

The edge block is the opposite case. The disc’s edge cuts across it on a slant, a jump in both directions. As 13.6 showed for a cliff in one row, a jump needs many cosines, and here it needs them across and down.

The flat number now holds only 31.1 % of the energy, and 27 numbers survive the rounding. Rebuilt and rounded, its pixels are within 35 levels of the original, with an RMS error of 15.7 levels.

So the bits go where the detail is. A smooth block costs two numbers, and a block with an edge costs many.

The maths behind it · a change of basis

The 64 cosine patterns of a block are an orthonormal basis of all 8 × 8 images: the DCT is a change of basis. Because the basis is orthonormal, an error in the coefficients is the same size as the error it makes in the pixels, energy for energy. For the edge block, before the decoder rounds to whole levels, both error energies are 17 020.6.

Fewer numbers, more blocks

One table gives one trade between size and error. JPEG lets you choose the trade with a single number.

Quality scales the table

The quality qq is a whole number from 1 to 100. The widely used library of the Independent JPEG Group turns it into a scale SS, in percent:

S={⌊5000/q⌋,q<50,200−2q,q≥50.S=\begin{cases}\lfloor 5000/q\rfloor, & q<50,\\ 200-2q, & q\ge50.\end{cases}

The brackets ⌊ ⌋\lfloor\ \rfloor mean “round down to a whole number”, as the library’s integer division does. At q=3q=3, for example, 5000/35000/3 is 1666.7, and SS is 1666.

Each step is the Annex K entry in the same place, written “table” below, scaled by SS percent. It is rounded to a whole number and kept between 1 and 255:

Δ[k1,k2]=⌊S⋅table+50100⌋.\Delta[k_1,k_2]=\left\lfloor\frac{S\cdot\text{table}+50}{100}\right\rfloor.

Adding 50 before rounding down makes the division round to the nearest whole number. The limits come from the file format: a step of 0 would mean dividing by 0, and baseline JPEG stores each step in one byte.

At q=50q=50, S=100S=100 and the table is unchanged. At q=90q=90, S=20S=20: the first step is 3 and the last is 20. At q=10q=10, S=500S=500: the first step is 80, and the last would be 495 but is held at 255. At q=100q=100, S=0S=0 and every step is 1.

Lower quality, coarser steps. Coarser steps round more numbers to 0, and the ones that survive are rounded more roughly.

Measuring the error

To compare qualities I need one number for the error. The mean-square error, MSE\mathrm{MSE}, is the average over all pixels of (decoded − original)², in levels squared.

In “Signal and noise, in decibels” of How big is a signal (1.3), a power ratio became decibels as 10log⁡1010\log_{10} of the ratio. Images use the largest possible pixel, 255, in place of the signal’s size. The result is the peak signal-to-noise ratio:

PSNR=10log⁡102552MSE dB.\mathrm{PSNR}=10\log_{10}\frac{255^2}{\mathrm{MSE}}\ \text{dB}.

Higher is better. Each 10 dB is a tenfold smaller MSE.

Measuring the size

How many bits would a file need? Count how often each stored number appears. A number that makes up a share pp of all of them can be given a code of about −log⁡2p-\log_2p bits. Common numbers get short codes, and rare ones long codes.

Averaged over all the numbers, that is the entropy:

H=−∑vpvlog⁡2pv,H=-\sum_v p_v\log_2p_v,

in bits per number, where pvp_v is the share of the numbers equal to vv. For the smooth block alone, with 62 zeros, one 3 and one −1, a zero costs 0.046 bits, each other value 6 bits, and HH is 0.23 bits per number.

The instrument counts all 16 384 stored numbers of the card in one histogram. Each block has 64 numbers for 64 pixels, so bits per number are bits per pixel. The card’s pixels need 8 bits each, so the compression ratio is 8 divided by the bits per pixel.

This is an estimate. A real JPEG file adds headers and uses Huffman codes, so its size differs; I come back to this after the instrument.

What to look for

When the steps get coarse, a block may keep little more than its flat number. It then turns into a flat square, and its neighbour into a slightly different flat square. Steps then appear at the block borders, where the original image had none. These are blocking artefacts.

JPEG codes brightness, called luminance, separately from colour, and this card has only brightness.

Fewer numbers, more blocks

The 128 × 128 test card through JPEG-style coding (luminance only); size is an entropy estimate.

Quality 90: 15.0 % of the numbers survive, 1.39 bits per pixel, PSNR 44.6 dB.

quality
90
PSNR
44.6 dB
nonzero numbers
15.0 %
size
1.39 (5.8 : 1)
0.00 / 13.00 s
Describe this picture

The 128 × 128 test card through JPEG-style coding (luminance only), with the size an entropy estimate. Two panels. The first shows the whole decoded card, with the zoom region outlined. The second, the zoom, shows rows 8 to 39 and columns 24 to 55, at the top edge of the disc, four times larger, with a thin grid every 8 pixels on the block borders. The readouts are the quality, the PSNR in dB, the nonzero numbers in percent, and the size in bits per pixel with its ratio to 8 bits per pixel. The 13 s clip opens at quality 90: 15.0 % of the numbers survive, 1.39 bits per pixel (5.8 : 1), PSNR 44.6 dB. At 3.5 s the picture cross-fades to quality 50, the standard table: 9.1 % survive, 0.81 bits per pixel (9.9 : 1), PSNR 34.5 dB. At 8 s it cross-fades to quality 10: 3.8 % survive, 0.35 bits per pixel (22.9 : 1), PSNR 28.3 dB, and the zoom shows the 8 × 8 grid as blocks. When the clip ends, a full-width slider, “Quality”, runs from 1 to 100, starting at 10 (arrow keys 1, Page Up and Page Down 10), and the caption gives the PSNR, the numbers that survive and the bits per pixel. The setting is kept in the link, as quality.q.

Watch the zoom as the quality falls from 90 to 10: it stays sharp at first, and at 10 the 8 × 8 grid shows through as blocks.

Notice the top strip of the zoom at quality 10, the gradient above the disc. It has turned into flat squares, and its grey changes only on grid lines.

Reading the numbers

Here is the whole range. I computed these seven settings of the slider:

qualityPSNR (dB)nonzero numbersbits per pixelratio
10066.224.1 %2.313.5 : 1
9044.615.0 %1.395.8 : 1
7538.010.9 %1.017.9 : 1
5034.59.1 %0.819.9 : 1
2531.57.5 %0.6512.3 : 1
1028.33.8 %0.3522.9 : 1
123.51.8 %0.1748.4 : 1

Down the table, as the quality falls, every column moves one way: fewer numbers, fewer bits, a lower PSNR. At quality 50 the card would take about 1655 bytes instead of 16 384.

Even quality 100 is not perfect. Its steps are all 1, but the DCT numbers are still rounded to whole numbers, so a little error remains: a PSNR of 66.2 dB, not an infinite one.

Now the blocks at quality 10. On the gradient, rows 0 to 7, the original climbs 0.70 levels per pixel. After coding, neighbouring pixels inside a block differ by 0.00 levels on average, and at the block borders by 5.33.

Where does that come from? At quality 10 the flat number’s step is 80. The flat number is 8 times the block’s mean, so a step of 80 moves the whole block by 10 levels. The finer numbers of these blocks all round to 0.

So each block on that strip is flat, and each border either jumps by 10 levels or not at all. Here 8 of the 15 borders jump, which averages 5.33. The gradient has become a staircase with treads one or two blocks wide.

The maths behind it · entropy

Entropy is information theory’s measure of surprise. A value that turns up often carries little news, and a rare one carries a lot. The fewer distinct values and the more zeros, the fewer bits the stored numbers need, which is why the bits per pixel fall with the quality.

The estimate looks at each stored number on its own. A real coder also uses structure: it writes runs of zeros as one symbol, and stores each block’s flat number as the change from the block before. So real JPEG files of this card would have different sizes. The trend with quality is the same.

Worked example

1. The smooth block’s flat number. The block’s mean is 133.625, so 5.625 after the shift. The flat number is 8 times that, 45.0. The step at quality 50 is 16, and 45.0/16=2.845.0/16=2.8, which rounds to 3.

2. One stripe across. The smooth block’s XDCT[1,0]X_\text{DCT}[1,0] is −12.31. Its step is 11, and −12.31/11=−1.12-12.31/11=-1.12 rounds to −1. The decoder gets back −1×11=−11-1\times11=-11, off by 1.31, less than half the step.

3. A step at quality 10. S=⌊5000/10⌋=500S=\lfloor5000/10\rfloor=500. The first table entry gives ⌊(500×16+50)/100⌋=80\lfloor(500\times16+50)/100\rfloor=80, five times the step at quality 50.

4. PSNR at quality 50. The MSE over the card is 22.82 levels squared. Then 2552/22.82=2849255^2/22.82=2849, and 10log⁡102849=34.510\log_{10}2849=34.5 dB.

5. Bits to ratio. At quality 50 the entropy is 0.81 bits per pixel. Against 8 bits per pixel, the ratio is 8/0.81=9.98/0.81=9.9.

Where you’ll meet this

JPEG photos, from cameras and on the web, are coded in 8 × 8 blocks as on this page. In colour, JPEG first turns red, green and blue into a brightness and two colour-difference images. It often stores the colour ones at half the resolution across and down, since the eye sees colour detail less sharply.

Video codecs code some frames as still images, the intra frames, in the same way. H.264’s basic transform is a 4 × 4 integer version of the DCT, so it can be computed exactly with whole numbers.

MP3 and AAC use a 1-D cousin, the MDCT, on overlapping blocks of sound. Perceptual audio coding (29.3) takes that up.

JPEG 2000 uses the wavelets of Wavelets (23.2), with “Keep the big coefficients, drop the rest” as its core idea. It transforms the whole image, so it has no blocks and no blocking artefacts. Its artefacts at low rates are blur and ringing near edges instead.

I left out colour, the Huffman codes, and progressive JPEG, which sends a rough image first and refines it. For more, see G. K. Wallace, “The JPEG still picture compression standard” (Communications of the ACM, 1991), the standard itself, ITU-T T.81, and Gonzalez and Woods, Digital Image Processing, chapter 8.

Reference card

QuantityFormulaNotes
Level shiftx[n1,n2]−128x[n_1,n_2]-128pixels 0 to 255
Block DCTorthonormal 2-D DCT-II of each 8 × 8 blockrows, then columns; energy kept
Flat numberXDCT[0,0]=8×X_\text{DCT}[0,0]=8\times block meanafter the shift
Quantiseround(XDCT[k1,k2]/Δ[k1,k2])\mathrm{round}\big(X_\text{DCT}[k_1,k_2]/\Delta[k_1,k_2]\big)error at most Δ/2\Delta/2 per number
Quality scaleS=⌊5000/q⌋S=\lfloor5000/q\rfloor (q<50q<50), 200−2q200-2q (q≥50q\ge50)IJG; q=50q=50 is Annex K’s table
Step⌊(S⋅table+50)/100⌋\lfloor(S\cdot\text{table}+50)/100\rfloor, 1 to 255q=10q=10: first step 80
Zig-zaglow to high frequencyends in a run of zeros
PSNR10log⁡10(2552/MSE)10\log_{10}(255^2/\mathrm{MSE})dB
Size estimateH=−∑vpvlog⁡2pvH=-\sum_vp_v\log_2p_v bits per pixelratio 8/H8/H

End of lesson 28.4

Where to go next.

Phasorium
LibraryEvery lesson, in order

Parts

About Phasorium
Look