This chapter links to the course’s interactive demos and to original papers on the open web, and it uses MathJax for its formulas. Read it through the course website (or download the file and open it in a browser); a Canvas inline preview will reach neither the demos nor the math. All chapters.

The Space of Images

There are 2^784, about 10^236, black-and-white 28 × 28 images. Almost none of them look like a digit. computed: 784 × log₁₀ 2

An image is a point. That sentence can be made precise in two ways, as a point of a finite-dimensional vector space with one axis per number, or as a function from the picture plane to color, and this chapter does both, shows how sampling connects them, and uses the function view to revisit aliasing and to introduce neural fields. It then does to images what linear algebra permits: changes their basis (the Fourier and cosine bases, with a transform worked by hand), convolves them with filters (a blur and an edge detector, worked on one pixel and applied to a whole render), builds pyramids of them, reshapes their histograms, compresses them the way JPEG does, and measures the distance between two of them with the two metrics every paper reports. Every claim about “the space” is then checked on a real dataset, 2,000 handwritten digits, with distances measured and pictures drawn. It closes with the fact that makes generation a sampling problem: the map from images to concepts is many-to-one, so its inverse is a distribution, and something must supply the missing choices.

Contents
  1. The finite-dimensional view
  2. The function-space view
  3. Sampling, bases, and aliasing
  4. The Fourier basis
  5. Filters: convolution
  6. Scale: image pyramids
  7. Histograms and equalization
  8. Compression: what JPEG keeps
  9. Comparing two images: PSNR and SSIM
  10. The thin sheet, measured
  11. The concept map is not invertible
  12. Exercises

1 · The finite-dimensional view

Definition (image as a vector) An \(m\times n\) RGB image is a point of the vector space \(\mathbb{R}^{3mn}\): list its numbers in a fixed order, \(\mathbf{x} = (r_{11}, g_{11}, b_{11}, r_{12}, \dots, b_{mn})\), and it is one point, or equivalently the vector from the origin to that point. Displayable images occupy the cube \([0,1]^{3mn}\). A \(128\times128\) RGB image has \(49{,}152\) coordinates; a \(512\times512\) one has \(786{,}432\).
A 20 by 20 handwritten 3 with a red 5 by 5 patch marked; the patch enlarged with each pixel's gray value printed in it; and the same 25 numbers written out as one vector.
The definition, on a real image. Left: a \(20\times20\) grayscale digit from the dataset the course’s demos use. Middle: the marked \(5\times5\) patch with each pixel’s value in \([0,1]\). Right: the same 25 numbers listed row by row are a vector in \([0,1]^{25}\); the whole digit is a vector in \([0,1]^{400}\). Nothing was lost in the listing, and nothing was added: the picture and the vector are the same object.

Everything the pipeline does to images is now linear algebra on that vector. Averaging two images is adding two vectors and halving; a blur is a linear operator (Section 5); the difference between two frames is a vector whose length is a measure of how much moved; the mean squared error between an image and its noisy version is \(\|\mathbf{x} - \mathbf{y}\|^2 / 3mn\), which is what the forward-process demo’s PSNR readout is built from (Section 9).

Worked example (the midpoint of two images) Take a 3 and an 8 from the dataset as vectors \(\mathbf{a}, \mathbf{b} \in [-1,1]^{400}\) (the demos store pixels in \([-1,1]\) rather than \([0,1]\); the geometry is the same). Their distance is \(\|\mathbf{a} - \mathbf{b}\| = 18.5\), which is \(0.925\) per pixel on average. The nine images below are \((1-t)\,\mathbf{a} + t\,\mathbf{b}\) for \(t = 0, \tfrac18, \dots, 1\).
Nine images blending linearly from a handwritten 3 to a handwritten 8; the middle ones show both digits superimposed as a double exposure.
The midpoint \(t = \tfrac12\) is a double exposure: both digits are visible at half strength, and it is not a digit anyone would write. Its nearest neighbor in the dataset (a 3) is 0.46 per pixel away, about as far as a real digit is from its own nearest neighbor (0.49, Section 10): the double exposure is no farther from the data than a digit is, and still is not one. The straight line between two meaningful images leaves the set of meaningful images almost immediately. That is the first sign that the set is curved: it is not a subspace, where averages of members are members.

The picture to keep is that this space is enormous and almost entirely empty of meaning. Draw a point uniformly at random from the cube and you get static, every time, because the set of images that look like anything is a thin, folded, low-dimensional sheet inside it. Generation is the problem of landing a new point on the sheet on purpose.

Top row: ten 20 by 20 images of uniform random noise. Bottom row: ten handwritten digits 0 through 9.
Top: ten points drawn uniformly at random from \([0,1]^{400}\). Bottom: ten points from the sheet, one per digit class. Both rows are vectors of exactly the same kind; the cube is full of the top row, and the bottom row is what the rest of this text is about.

2 · The function-space view

The vector view has a resolution baked in. A cleaner object is the image as a function: a rule that assigns a color to every point of the picture plane.

Definition (image as a function) A continuous image is a function \(f : [0,1]^2 \to [0,1]^3\), from the unit square to RGB. The set of all such functions with finite energy, \[ \mathcal{I} \;=\; \Big\{ f : [0,1]^2 \to \mathbb{R}^3 \;\Big|\; \int_{[0,1]^2} \|f(u,v)\|^2\, du\, dv < \infty \Big\}, \] is a vector space (the sum of two images and a scalar multiple of an image are images), with the inner product and norm \[ \langle f, g\rangle = \int_{[0,1]^2} f(u,v)\cdot g(u,v)\, du\, dv, \qquad \|f\| = \sqrt{\langle f, f\rangle}. \] It is infinite-dimensional: no finite list of numbers pins down an arbitrary \(f\).
Worked example (two images, one distance) Let \(f\) be uniform mid-gray, \(f(u,v) = (\tfrac12,\tfrac12,\tfrac12)\), and \(g\) a horizontal black-to-white ramp, \(g(u,v) = (u,u,u)\). Then \[ \begin{aligned} \|f - g\|^2 &= 3\int_0^1\!\!\int_0^1 (\tfrac12 - u)^2\, du\, dv = 3\int_0^1 (\tfrac14 - u + u^2)\, du \\ &= 3\,\big(\tfrac14 - \tfrac12 + \tfrac13\big) = 3\cdot\tfrac{1}{12} = \tfrac14, \end{aligned} \] so \(\|f-g\| = \tfrac12\). Sample both on an \(m\times n\) grid and the per-pixel mean squared error of the rasters converges to the same \(\tfrac{1}{4}\) as \(m\to\infty\): the finite-dimensional distance is the Riemann sum of the function-space one. Resolution changes the count of numbers, not the geometry.

Why bother with the function view in a course that renders rasters? Three reasons that pay off within this text. First, several of the pipeline’s operations are stated most honestly on functions: anti-aliasing is filtering before sampling (the antialiasing chapter); a texture is a function on \([0,1]^2\) that the UV map pulls onto a surface (the texture chapter), and a mipmap is that function at several bandwidths. Second, a scene can be a function too: the learned-scenes chapter’s NeRF is a network standing in for \(F : \mathbb{R}^3\times S^2 \to \mathbb{R}^3\times\mathbb{R}_{\ge 0}\), color and density at every point and viewing direction, with no mesh anywhere, and the same idea (a network as a coordinate-to-value function, an implicit or neural field representation) now represents images, shapes, and volumes at any resolution. Third, the generative models of the diffusion chapter are stated on vectors of a fixed size, but what they learn is resolution-agnostic in spirit: the sheet of natural images is a property of the functions, and a model trained at one raster size approximates the same sheet a model at another size approximates. Diffusion on function space directly is an active research direction; this text stays in \(\mathbb{R}^{3mn}\), where every statement is literal.

3 · Sampling, bases, and aliasing

The two views are related by sampling. An \(m\times n\) raster is the function evaluated on a grid, \(\mathbf{x}_{ij} = f(u_i, v_j)\) with \(u_i = (i - \tfrac12)/m\) and \(v_j = (j-\tfrac12)/n\); this is the “one look per pixel at its center” of the rasterization chapter. Going the other way, a raster defines a function by reconstruction: the nearest-neighbor rule gives a piecewise-constant \(f\) (a mosaic of \(3mn\) little box functions with the pixel values as coefficients), bilinear interpolation gives a continuous one, and a band-limited reconstruction gives the smoothest.

Worked example (sampling a stripe pattern) A gray image is \(f(u,v) = \tfrac12 + \tfrac12\cos(2\pi k u)\), vertical stripes with \(k\) periods across the picture. Sample it on 8 pixel centers \(u_i = (i - \tfrac12)/8\).
Three panels for k = 2, 4 and 8 periods: the continuous cosine in gray, eight sample points in blue, the reconstructed piecewise-constant raster in red, and the 8-pixel raster drawn as gray cells below each panel. For k = 2 the raster shows stripes; for k = 4 it is uniform mid-gray; for k = 8 it is uniformly black.
\(k\)the eight samplesthe raster
20.854, 0.146, 0.146, 0.854, 0.854, 0.146, 0.146, 0.854two periods of stripes at amplitude 0.707: survives
40.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5every sample at a zero crossing: uniform gray, the stripes are gone
80, 0, 0, 0, 0, 0, 0, 0every sample at a trough: uniform black
With \(k = 8\), \(2\pi k u_i = 2\pi(i - \tfrac12)\) and the cosine is \(-1\) at every sample; with \(k = 4\) it is \(\cos(\pi(i-\tfrac12)) = 0\). Eight periods on eight pixels is at the sampling rate and four is at half of it, the Nyquist limit; anything at or above it is not represented and can masquerade as something else. Supersampling or a blur before sampling removes the frequencies the grid cannot carry instead of letting them alias.
Aliasing, revisited Sampling is a linear map from \(\mathcal{I}\) to \(\mathbb{R}^{3mn}\) and it throws information away: two different functions (the \(k = 4\) stripes and a flat gray) produce the same raster. When the discarded part matters (a checkerboard finer than the pixel grid, an edge crossing pixels at an angle) the samples tell a false story. That is aliasing, the disease of the raster-machines era, and the sampling theorem says exactly when it cannot happen: a function with no frequencies above \(B\) is recovered perfectly from samples spaced closer than \(1/2B\). Supersampling, mipmaps, and motion blur are all ways of removing frequencies the grid cannot carry before sampling, which is why they work. The antialiasing chapter is this paragraph made a subject.

In this language the vector \(\mathbf{x}\in\mathbb{R}^{3mn}\) is the list of coefficients of \(f\) in the box-function basis, and choosing a resolution is choosing how many basis functions to keep. Other bases exist and matter. The Fourier basis (sines and cosines of increasing frequency, Section 4) is the one in which JPEG stores images, keeping the low-frequency coefficients and discarding the rest (Section 8); wavelet bases organize detail by scale (Section 6). An image is the same function in every basis; only the numbers change.

An 8 by 8 grid of 8 by 8 patches: the two-dimensional cosine basis, from a flat patch at top left to a fine checkerboard at bottom right, with horizontal frequency increasing across and vertical frequency increasing down.
The 64 basis images of the \(8\times8\) discrete cosine transform, the basis JPEG uses. Horizontal frequency increases across, vertical frequency down. Any \(8\times8\) patch is a weighted sum of these 64 pictures, exactly as it is a weighted sum of 64 single-pixel pictures; JPEG keeps the weights near the top left and rounds the rest toward zero.
Worked example (the same digit in the cosine basis) Transform the \(20\times20\) digit of Section 1 into the cosine basis (400 coefficients) and rebuild it from only the \(K\times K\) lowest frequencies.
Seven reconstructions of the handwritten 3 from its lowest cosine coefficients: a flat gray for one coefficient, a soft blob for four, a blurry shape for nine, a recognizable blurry 3 for 25, a clear 3 for 64 and 144, and the exact digit for all 400.
coefficients kept1492564144400
RMS error per pixel0.430.410.360.310.150.090
One coefficient is the mean gray. Nine give a blob where the digit is. Sixty-four of four hundred give a 3 anyone can read. The pixel basis spends its 400 numbers evenly over the picture; the cosine basis puts what matters first, which is what a compressor wants and, as the diffusion chapter shows, what a noise schedule does to a picture in time: the coarse structure survives longest.

4 · The Fourier basis

Definition (the two-dimensional discrete Fourier transform) For an \(m\times n\) image \(x_{ij}\), the coefficient at horizontal frequency \(u\) and vertical frequency \(v\) is \[ X_{uv} = \sum_{i=0}^{m-1}\sum_{j=0}^{n-1} x_{ij}\, e^{-2\pi\mathrm{i}\,(ui/m + vj/n)}, \qquad u = 0..m-1,\; v = 0..n-1, \] a complex number whose magnitude \(|X_{uv}|\) is the amplitude of a sinusoidal stripe pattern with \(u\) periods across and \(v\) down, and whose phase is that pattern’s offset. The transform is invertible, \(x = \tfrac{1}{mn}\sum X_{uv}\,e^{+2\pi\mathrm{i}(\cdots)}\), and it is orthogonal up to a constant, so \(\sum |x_{ij}|^2 = \tfrac{1}{mn}\sum |X_{uv}|^2\) (Parseval): the energy of the picture is the energy of its spectrum. The cosine transform of Section 3 is the same idea restricted to real cosines with even symmetry, which is why it needs no complex numbers and why JPEG chose it.
Worked example (a \(4\times4\) stripe pattern) The image \(x_{ij} = \tfrac12 + \tfrac12\cos(2\pi i/4)\), one period of vertical stripes across four columns, has rows \((1, \tfrac12, 0, \tfrac12)\). Its transform has exactly three nonzero coefficients: \(|X_{00}| = 8\) (the sum of all sixteen pixels, the mean times \(mn\)), and \(|X_{10}| = |X_{30}| = 4\), the stripe at frequency 1 and its mirror at frequency \(4 - 1 = 3\). Every \(v \neq 0\) coefficient is zero because nothing varies down the columns. Sixteen numbers became three, which is the whole reason to change basis: a pattern that is spread over every pixel is concentrated on a few frequencies.
Four panels: the handwritten 3; its log-magnitude spectrum with the zero frequency at the center, bright in the middle and dim toward the edges; the Cornell render in gray; and its spectrum, a bright center with streaks along the axes and along the diagonals of the box edges.
A digit and a render with their spectra, \(\log(1 + |X_{uv}|)\), zero frequency at the center. The digit’s energy sits in the middle: the 49 coefficients with \(|u|, |v| \le 3\) hold 89.6 % of it, which is the DCT rebuild table of Section 3 seen from the other side. The render’s spectrum shows streaks perpendicular to the box’s edges, because a sharp edge is a sinusoid at every frequency along the direction it crosses.

Three facts of the Fourier view organize the rest of the chapter. A sharp edge has energy at every frequency, so any operation that removes high frequencies blurs edges (Section 5). Sampling a picture at \(m\) pixels keeps only frequencies below \(m/2\), and folds the rest back onto lower ones (Section 3, and the mirror pair \(X_{10}, X_{30}\) above is that folding). And a convolution in the picture is a multiplication in the spectrum, which makes the next section’s filters easy to reason about and, for large kernels, fast to compute by transforming, multiplying and transforming back.

5 · Filters: convolution

Definition (convolution, kernel) Convolving an image with a kernel \(K\) (a small array, here \(3\times3\)) replaces each pixel by the weighted sum of its neighborhood, \[ y_{ij} = \sum_{a=-1}^{1}\sum_{b=-1}^{1} K_{ab}\, x_{i+a,\,j+b}, \] with the picture’s edge pixels repeated beyond the border. It is a linear map on the image vector, the same everywhere, and every linear, shift-invariant operation on an image is a convolution with some kernel.

Two kernels do most of the work of image processing. The Gaussian blur, here the \(3\times3\) binomial approximation \(\tfrac{1}{16}\begin{pmatrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \end{pmatrix}\), averages each pixel with its neighbors, weighted by distance, and removes high frequencies: it is the prefilter of the antialiasing chapter and the first step of Section 6’s pyramid. The Sobel operators, \(S_x = \begin{pmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{pmatrix}\) and its transpose \(S_y\), estimate the horizontal and vertical derivatives of the picture; their magnitude \(\sqrt{g_x^2 + g_y^2}\) is an edge map and their ratio the edge’s direction.

Worked example (one pixel, three kernels) At pixel \((12, 15)\) of the digit the \(3\times3\) neighborhood has three identical rows \((0.99, 0.29, 0)\): ink on the left, paper on the right, a vertical edge. Gaussian: \(\tfrac{1}{16}\big[(1 + 2 + 1)(0.99) + (2 + 4 + 2)(0.29) + (1 + 2 + 1)(0)\big] = \tfrac{1}{16}(3.96 + 2.32) = 0.392\), between the stroke and the paper, which is what a blur does to an edge. Sobel \(x\): \((-1 - 2 - 1)(0.99) + 0 + (1 + 2 + 1)(0) = -3.96\); Sobel \(y\): the rows are equal, so the top and bottom rows cancel, \(-0.004\). Magnitude 3.96, direction \(\arctan2(-0.004, -3.96) = 180^\circ\): the gradient points left, from paper toward ink, and the edge runs vertically. The same kernel, applied as a learned layer, is the convolution example of the networks chapter.
Three 20 by 20 panels: the handwritten 3, the 3 blurred by the Gaussian kernel, and its Sobel edge magnitude, a bright outline of the stroke.
The digit, blurred, and its edge magnitude.
Three 128 by 128 panels: the course's Cornell render in gray, the same blurred, and its Sobel edge magnitude showing the box outline, the block, the bunny and the teapot as bright lines.
The same two kernels on the course’s \(128\times128\) Cornell render (luminance only). The blur is one pass; the edge map is the magnitude of the two Sobel responses, normalized to its maximum.

Larger kernels blur more (a Gaussian of standard deviation \(\sigma\) needs a kernel about \(6\sigma\) wide), and the blur can be applied as two one-dimensional passes because the Gaussian is separable, which is what a GPU post-process does. Sharpening is the original minus a blur (an unsharp mask); a median filter, which is not linear, removes salt-and-pepper noise that a blur would smear. And deconvolution, undoing a known blur, is possible only where the blur’s spectrum is not zero, which for a Gaussian is nowhere exactly and everywhere badly: a blurred edge cannot be recovered from the blur alone, which is where the priors of the diffusion chapter come in.

6 · Scale: image pyramids

A picture has content at every scale, and many operations want to work scale by scale: a mipmap is the texture at several scales, a coarse-to-fine registration matches large structures first, and the U-Net of the diffusion chapter is a pyramid with learned filters. The two classic pyramids are built from Section 5’s blur.

Definition (Gaussian and Laplacian pyramids) The Gaussian pyramid \(G_0, G_1, \dots\) starts from the image and repeatedly blurs and drops every other row and column, halving the size at each level. The Laplacian pyramid stores at each level what the next coarser level lost, \(L_k = G_k - \mathrm{up}(G_{k+1})\), with \(\mathrm{up}\) bilinear upsampling; the coarsest Gaussian level is kept as is. The image is recovered exactly by working back up, \(G_k = L_k + \mathrm{up}(G_{k+1})\).
Top row: the Cornell render at 128, 64, 32 and 16 pixels. Bottom row: the three Laplacian levels, gray images with the edges of the scene as bright and dark lines at three scales, and the 16-pixel coarsest level at the end.
The render’s Gaussian pyramid (top) and Laplacian pyramid (bottom; mid-gray is zero). The Laplacian levels are band-pass: each holds the detail between two scales, and their root-mean-square values, 0.041, 0.049 and 0.064, grow toward the coarse end because the scene’s contrast lives in its large structures.
Worked example (the cost of a pyramid, and the exactness of the rebuild) Four levels of a \(128\times128\) image hold \(128^2 + 64^2 + 32^2 + 16^2 = 21{,}760\) pixels, \(1.33\) times the original, the same \(\tfrac43\) as a mipmap (the texture chapter). Rebuilding \(G_0\) from \(L_0, L_1, L_2\) and \(G_3\) returns the original with a maximum error of 0 in floating point: nothing was discarded, only rearranged by scale. That is what makes the Laplacian pyramid a transform rather than a summary, and what lets a compressor quantize each level differently: the coarse levels, few pixels and high energy, get the bits.
Two 64-pixel versions of the Cornell render side by side: the left, made by keeping every other pixel, has ragged edges and speckle on the brick texture; the right, made by blurring first, is smooth.
The same render halved two ways. Left: every other pixel kept, the box filter skipped, which is the sampling error of the antialiasing chapter in miniature; right: the 2×2 average that the pyramid uses. Against the exact area average, the dropped version scores 26.9 dB and the blurred one 29.4 dB, and the difference is visible in the brick and along every edge.

7 · Histograms and equalization

Forgetting where the pixels are leaves their distribution. The histogram of an image counts pixels by value; it says nothing about the picture’s content and a great deal about its exposure, contrast and dynamic range. The render below has 28.6 % of its pixels below 0.25 (the black background outside the box) and 65.6 % below 0.5: it is a dark image with a bright light panel, a typical rendering before tone mapping.

A histogram of the render's gray levels in 32 bins, blue before equalization with a tall spike near black and a hump around 0.45, orange after equalization spread evenly across the range with a spike at 0.28 where the black spike landed; the cumulative distribution drawn as a black curve.
The render’s histogram before (blue) and after equalization (orange), with the cumulative distribution that defines the mapping. The black spike cannot be spread, because equalization is a function of the value: every pixel at the same gray goes to the same place.
Definition (histogram equalization) Map each pixel value \(v\) to \(C(v)\), the fraction of pixels at or below \(v\) (the cumulative distribution). The result’s histogram is as flat as the data allow, and the mapping is monotone, so nothing is reordered: brighter stays brighter.
Worked example (four values through the map) From the render’s cumulative distribution in 32 bins: \(0.1 \mapsto 0.28\) (the black background lands at 0.28, since 28 % of pixels are at or below it), \(0.3 \mapsto 0.29\) (almost nothing lies between 0.1 and 0.3, so the two values nearly coincide), \(0.5 \mapsto 0.71\), \(0.8 \mapsto 0.96\). The mean rises from 0.404 to 0.555. The middle of the range, where most of the box’s walls sit, is stretched from \([0.3, 0.5]\) to \([0.29, 0.71]\), which is why the equalized render shows the walls’ shading more strongly and the background as a flat gray.
The Cornell render in gray beside its histogram-equalized version, in which the walls' shading is more pronounced and the black background has become a mid-dark gray.
Before and after. Equalization is a global tone curve computed from the image itself; a local version (CLAHE) equalizes tiles separately with limits on the slope, and a tone-mapping operator (the illumination chapter) is a tone curve chosen for a display rather than for a flat histogram.

8 · Compression: what JPEG keeps

Section 3 showed that a digit is mostly a few low-frequency cosine coefficients. JPEG is that observation made an algorithm: cut the image into \(8\times8\) blocks, transform each block into the cosine basis, divide every coefficient by an entry of a quantization table that grows toward high frequencies, round to an integer, and store the mostly-zero result compactly. The table is where the quality setting lives: larger divisors, more zeros, smaller files, more error.

Worked example (one block at quality 50) The \(8\times8\) block at \((40, 72)\) of the render (a patch of the back wall crossing the box’s edge), pixel values 86 to 116 on the 0–255 scale. Subtract 128 and transform: the DC coefficient is \(-193.8\) and the largest others are \(-50.9\) at vertical frequency 5 (a horizontal stripe) and \(17.5\), \(-15.7\) at low mixed frequencies. Divide by the standard luminance table (16 at DC, 10 to 24 across the low frequencies, 99 to 121 in the corner) and round: \[ \begin{pmatrix} -12 & 0 & 0 & 0 & \cdots \\ 1 & -1 & 0 & 0 & \cdots \\ -1 & 1 & -1 & 0 & \cdots \\ 0 & 0 & 0 & 0 & \cdots \\ 0 & \cdots \\ -2 & 0 & \cdots \\ 0 & \cdots \\ 0 & \cdots \end{pmatrix} \] Seven nonzero integers of 64. Multiply back by the table and invert the transform: the block is reconstructed with a root-mean-square error of 4.4 gray levels and a maximum error of 12, on the edge pixels. Over the whole render at quality 50 the blocks average 7.7 nonzero coefficients of 64 and the image is reproduced at 34.3 dB PSNR (Section 9); at quality 90, 16.2 coefficients and 42.5 dB; at quality 10, 3.3 coefficients and 28.3 dB, with the block boundaries visible.
The Cornell render in gray four times: original, and after JPEG-style quantization at quality 90, 50 and 10; the last shows visible 8 by 8 blocks and ringing at the box edges.
The render, then the render after cosine-transform quantization at quality 90, 50 and 10, reconstructed by the generator’s own transform. The entropy coding that turns the sparse integers into bytes is not simulated; the count of nonzero coefficients per block stands in for the file size.

Everything visual about JPEG follows from the block transform: the \(8\times8\) blocking at low quality, because each block is quantized on its own; the ringing beside sharp edges, because an edge needs high frequencies that were rounded away (Section 4); and the softness of fine texture, for the same reason. Video codecs add prediction from neighboring blocks and previous frames and transform the residual; the wavelet-based JPEG 2000 replaces blocks with a pyramid like Section 6’s and has no blocking. Texture compression on the GPU (BC7, ASTC) uses fixed-rate blocks so that a texel can be fetched without decoding its neighbors, at the cost of a worse ratio.

9 · Comparing two images: PSNR and SSIM

Definition (PSNR, SSIM) For images \(\mathbf{x}, \mathbf{y}\) with values in \([0, 1]\), the peak signal-to-noise ratio is \[ \text{PSNR} = 10\log_{10}\frac{1}{\text{MSE}}, \qquad \text{MSE} = \frac{1}{N}\|\mathbf{x} - \mathbf{y}\|^2, \] in decibels: 20 dB is an RMS error of 0.1 per pixel, 40 dB of 0.01. The structural similarity index (Wang and others, 2004) compares local means, variances and covariance, \[ \text{SSIM} = \frac{(2\mu_x\mu_y + C_1)(2\sigma_{xy} + C_2)}{(\mu_x^2 + \mu_y^2 + C_1)(\sigma_x^2 + \sigma_y^2 + C_2)}, \] with \(C_1 = 0.01^2\), \(C_2 = 0.03^2\), computed in a sliding window and averaged; it is 1 for identical images and near 0 for unrelated ones. This chapter computes it over the whole \(20\times20\) digit as one window.
Worked example (four comparisons with one digit)
Five 20 by 20 panels: the handwritten 3; the same 3 with noise added; the same 3 blurred; the same 3 shifted right by one pixel; and a handwritten 8.
compared with the 3PSNR (dB)SSIM
the same 3, noised at \(t = 0.3\) of the diffusion schedule14.10.880
the same 3, blurred once18.80.958
the same 3, shifted right by one pixel12.70.853
a handwritten 86.70.366
For the noised digit: \(\mu_x = 0.343\), \(\mu_y = 0.380\), \(\sigma_x^2 = 0.184\), \(\sigma_y^2 = 0.139\), \(\sigma_{xy} = 0.143\), so \(\text{SSIM} = (2\cdot0.343\cdot0.380 + 0.0001)(2\cdot0.143 + 0.0009)/\big((0.118 + 0.145 + 0.0001)(0.184 + 0.139 + 0.0009)\big) = 0.880\). The 8 scores lowest on both, as it should. But the shifted copy, which any person would call the same picture, scores lower on PSNR than the visibly noisy one and about the same on SSIM: pixel-wise metrics compare pixels at the same position, and a one-pixel shift moves every stroke edge onto paper.
Pitfall Two ways a metric misleads, with the numbers above.

Learned metrics (LPIPS: distances between the feature maps of a trained convolutional network, Section 6 of the networks chapter) track human judgments better than either, at the price of depending on what the network was trained on. Every paper on learned scenes reports all three, and the learned-scenes chapter’s comparison table quotes them.

Hands-on The digits are a binary file the course library reads. From css551/:
  1. Measure the distance between two digits and find one digit’s nearest neighbor among the other 1,999:
    const fs=require('fs');import('./lib/core/diffusion-nd.js').then(nd=>{
      const bytes=new Uint8Array(fs.readFileSync('lib/assets/mnist/mnist-2000-20x20.bin'));
      const labels=new Uint8Array(fs.readFileSync('lib/assets/mnist/mnist-2000-labels.bin'));
      const data=nd.makeDataset(bytes,labels,400);const a=data.x.subarray(0,400),b=data.x.subarray(400,800);
      let s=0;for(let j=0;j<400;j++)s+=(a[j]-b[j])**2;
      console.log('labels',labels[0],labels[1],'distance',Math.sqrt(s).toFixed(2),'perPixel',Math.sqrt(s/400).toFixed(3));
      const idx=nd.nearestIndex(a,{x:data.x.subarray(400),y:null,n:1999,d:400});
      console.log('nearest other to digit 0: index',idx.index+1,'label',labels[idx.index+1],'dist/px',idx.dist.toFixed(3));});
    Expected: labels 0 1 distance 21.48 perPixel 1.074 and nearest other to digit 0: index 610 label 0 dist/px 0.400. A 0 and a 1 are farther apart than a typical pair (Section 10’s 20.1); digit 0’s nearest neighbor is another 0, at 0.40 per pixel.
  2. Reproduce the cosine-basis table of Section 3 for one more \(K\): write the \(20\times20\) DCT as two nested loops over \((u, v)\) with \(\cos\big((2i+1)u\pi/40\big)\cos\big((2j+1)v\pi/40\big)\) weights, zero all coefficients outside the lowest \(K\times K\), invert, and print the RMS error against the original. At \(K = 10\) (100 coefficients) the error falls between the table’s 0.15 (\(K = 8\)) and 0.09 (\(K = 12\)).
  3. In the dissolving-picture demo, set \(t = 0.3\) and read the PSNR: it is the render’s analogue of the 14.1 dB row above, and the demo computes it with the formula of this section.

10 · The thin sheet, measured

Whatever the view, the images that mean something form a small region: a curved, low-dimensional set often called the manifold of natural images, though it is neither smooth nor a single piece. Two facts about it make generation possible. It is thin: nearly every direction away from a photograph leads immediately to something that is not a photograph, which is why random points are static. And it is structured: moving along it changes content in ways that have names (a cat turns its head, the light warms, the lens zooms), so the sheet has coordinates you can say in words, which is exactly what the encoder of the networks chapter computes. Both facts can be measured on the 2,000 digits.

A histogram of the Euclidean distances between pairs of digits in 400 dimensions: pairs from the same class peak near 17 and pairs from different classes near 20, with wide overlap.
All \(1{,}999{,}000\) pairwise distances between the 2,000 digits (pixels in \([-1,1]\)). Same-class pairs average 17.4, different-class pairs 20.1. Because a digit is nearly binary, a squared distance is about four times the number of pixels where the two disagree: same-class pairs disagree on about 79 pixels, different-class pairs on about 103.
Worked example (how thin is thin?) Three distances, all in the same units. So the sheet is a set where every point has a neighbor at 10 while the surrounding space sits at 23: a thin ribbon of digits threading a cube of static. (Two random points of the cube are only 16.3 apart on average, \(\sqrt{400\cdot\tfrac23}\); noise is close to noise. It is the digits that are far from noise.)
A scatter plot of 2,000 digits projected onto their two principal axes, colored by class: the ones cluster tightly on the left, zeros spread to the right, and the other classes form overlapping clouds.
The sheet has structure: the 2,000 vectors of \(\mathbb{R}^{400}\) projected onto the two directions of greatest variance (10 % and 7 % of the total; the shadow is crude, and the classes overlap far less in 400 dimensions than here). The 1s, thin and vertical, sit alone at the left; the 0s, wide loops, at the right; the axis between them is roughly “how much ink”. A learned encoder finds coordinates like these, but many more of them and nonlinear.

11 · The concept map is not invertible

Left: image space drawn as a box with a curved green sheet of natural images and five blue cat photographs on it. Right: concept space with a single purple point labeled cat, and all five arrows from the photographs converging on it.
Five different photographs, one embedding. The encoder \(E\) is many-to-one by construction.

Let \(E\) be the image encoder. It is many-to-one by design: thousands of cat photographs land on one “cat” embedding, because throwing away which cat, which pose, and which light is the whole point of a concept. So \(E\) has no inverse function. The honest inverse of a concept is a conditional distribution \(p(\mathbf{x}\mid c)\) over images, and a generator has to draw from it. A decoder that took only the concept would be forced to return one image per caption forever (which is what early text-to-image systems did); something must supply the missing choices. Diffusion (its own chapter) supplies them as noise: every sample starts from a different random point \(\mathbf{x}_T \sim \mathcal{N}(0, I)\), which contains more than enough arbitrary information, and the model spends its steps turning that noise into a specific image consistent with \(c\). Different noise, different cat; the caption steers, the noise decides. A one-to-many problem becomes a sequence of one-to-one steps, each of which a network can learn.

Worked example (counting the missing choices) A CLIP embedding has 512 numbers; a \(512\times512\) image has 786,432. Even if every embedding coordinate carried a full number of information about the picture, the caption side fixes at most 512 of the 786,432 coordinates, less than a thousandth. The other 785,920 numbers are the “which cat, which pose, which light” that the concept discarded. The starting noise \(\mathbf{x}_T\) has 786,432 random numbers: exactly enough to fill every choice the caption left open. That is not a coincidence of the design; it is the reason the design works.

The same asymmetry appears in every inverse problem of this chapter. Deconvolution (Section 5) cannot recover the high frequencies a blur removed; JPEG decoding (Section 8) cannot recover the coefficients that were rounded to zero; upsampling a Gaussian pyramid level cannot recover its Laplacian. Each is many-to-one, and each has the same honest answer: a distribution over the inputs that could have produced the output, from which a prior over natural images picks the plausible ones. The denoiser of the diffusion chapter is that prior in computable form, and denoising, deblurring, inpainting and super-resolution are all the same problem posed to it.

12 · Exercises

Exercise 1 (dimension) A 1080p RGB frame is \(1920\times1080\). What is the dimension of the vector space it lives in? If a video is 30 frames per second, what is the dimension of one second of video?
Solution \(3\cdot1920\cdot1080 = 6{,}220{,}800\). One second is 30 such vectors stacked, \(186{,}624{,}000\) coordinates: the “3D block of latents” of video diffusion is a block of exactly this shape, before the autoencoder shrinks it.
Exercise 2 (function space) Let \(f(u,v) = (u, 0, 0)\) and \(g(u,v) = (0, v, 0)\) on \([0,1]^2\). Compute \(\langle f, g\rangle\), \(\|f\|\), and \(\|f - g\|\).
Solution \(\langle f,g\rangle = \int\!\!\int (u\cdot0 + 0\cdot v + 0)\,du\,dv = 0\): the images are orthogonal (no channel overlaps). \(\|f\|^2 = \int_0^1\!\!\int_0^1 u^2\,du\,dv = \tfrac13\), so \(\|f\| = 1/\sqrt3\); by symmetry \(\|g\| = 1/\sqrt3\), and \(\|f-g\|^2 = \|f\|^2 + \|g\|^2 = \tfrac23\).
Exercise 3 (sampling and aliasing) Repeat the stripe example with \(k = 6\) on the same 8-pixel grid. What does the raster show, and what frequency does it appear to have?
Solution \(2\pi\cdot6\cdot(i-\tfrac12)/8 = \tfrac{3\pi}{2}(i - \tfrac12)\); the samples are \(\tfrac12 + \tfrac12\cos\) of \(\tfrac{3\pi}{4}, \tfrac{9\pi}{4}, \tfrac{15\pi}{4}, \dots\), which is \(0.146, 0.854, 0.854, 0.146, 0.146, 0.854, 0.854, 0.146\): the \(k = 2\) pattern, shifted. Six periods above the Nyquist limit of four alias to \(8 - 6 = 2\) periods. The raster cannot tell \(k = 6\) from \(k = 2\), which is what aliasing means.
Exercise 4 (the Fourier transform) Compute the \(4\times4\) transform of the horizontal-stripe image \(x_{ij} = \tfrac12 + \tfrac12\cos(2\pi j/4)\), and of the checkerboard \(x_{ij} = \tfrac12 + \tfrac12(-1)^{i+j}\).
Solution The horizontal stripes are the worked example transposed: \(|X_{00}| = 8\), \(|X_{01}| = |X_{03}| = 4\), all else zero. The checkerboard is \(\tfrac12 + \tfrac12\cos(\pi i)\cos(\pi j)\) up to sign, at frequency 2 in both directions: \(|X_{00}| = 8\) and \(|X_{22}| = 8\), the highest frequency the grid can hold on each axis. Sample the checkerboard on a \(2\times2\) grid and \(X_{22}\) aliases onto \(X_{00}\): a uniform gray.
Exercise 5 (a kernel by hand) Apply the Gaussian kernel and \(S_y\) to a neighborhood whose columns are all \((0, 0.5, 1)\) from top to bottom (a horizontal edge).
Solution Gaussian: \(\tfrac{1}{16}\big[4(0) + 8(0.5) + 4(1)\big] = 0.5\), the edge’s midpoint. \(S_y\): top row \(-(1 + 2 + 1)(0) = 0\), bottom row \((1 + 2 + 1)(1) = 4\), total \(+4\): a gradient pointing down, from dark to bright; \(S_x\) gives 0 because every row is constant. The edge magnitude is 4, the maximum a unit step can give, and the direction is \(90^\circ\).
Exercise 6 (pyramids) How many pixels does a full Gaussian pyramid of a \(1024\times1024\) image hold, down to \(1\times1\)? If a Laplacian pyramid is stored with 8 bits per pixel at every level, how does its size compare with the image, and why would a compressor still use it?
Solution \(\sum_{k=0}^{10} 4^k = 1{,}398{,}101\) pixels, \(1.333\) times the image (the same series as the mipmap). Stored losslessly it is a third larger; a compressor uses it because the Laplacian levels are mostly near zero (mid-gray in the figure) and quantize to few bits, and because the coarse levels, which carry the energy, are tiny. Wavelet codecs are this idea with better filters.
Exercise 7 (equalization) An image has half its pixels at 0.2 and half at 0.8. What does equalization do to it, and what does it do to an image whose histogram is already flat?
Solution \(C(0.2) = 0.5\), \(C(0.8) = 1\): the two values become 0.5 and 1.0. The contrast between them shrinks from 0.6 to 0.5 and the image brightens; equalization cannot spread a two-valued image over the range, since it maps values, not pixels. A flat histogram has \(C(v) = v\): the identity. Equalization is a fixed point on already-equalized images, which is one way to see that it is the right map.
Exercise 8 (JPEG) The block of Section 8 has a coefficient of \(-50.9\) at vertical frequency 5, horizontal 0, whose table entry is 24. What does it quantize to, what does the reconstruction lose there, and what would the entry be at quality 10 (table scaled by 5)?
Solution \(\text{round}(-50.9/24) = -2\), reconstructed as \(-48\), an error of 2.9 in that coefficient. At quality 10 the entry is 120, the coefficient rounds to 0, and the block loses its horizontal stripe entirely: the 2.5-cycle pattern across the block, the box edge, blurs into the wall. That is the smoothing visible in the quality-10 panel.
Exercise 9 (PSNR arithmetic) Two renders differ by a uniform offset of 0.05 in every pixel. What is their PSNR? What is it if only one pixel in a hundred differs, by 0.5?
Solution Uniform offset: MSE \(= 0.0025\), PSNR \(= 26.0\) dB. Sparse error: MSE \(= 0.01\times0.25 = 0.0025\), the same 26.0 dB. A slight global tint and a scattering of badly wrong pixels are indistinguishable to PSNR, and SSIM separates them (the offset changes \(\mu\) a little and nothing else; the sparse errors change the covariance).
Exercise 10 (the midpoint, in general) Two images on the sheet are \(\mathbf{a}\) and \(\mathbf{b}\), and their midpoint \(\tfrac12(\mathbf{a} + \mathbf{b})\) is not on the sheet. Show that the midpoint is nevertheless closer to each of them than they are to each other, and say why that does not make it a plausible image.
Solution \(\|\tfrac12(\mathbf{a}+\mathbf{b}) - \mathbf{a}\| = \tfrac12\|\mathbf{b} - \mathbf{a}\|\), half the distance, by algebra alone. Distance in \(\mathbb{R}^{3mn}\) measures pixel-wise disagreement, not plausibility: the double exposure disagrees with each parent by half as much as they disagree with each other, and still looks like neither. Membership of the sheet is not a matter of being near a member; it is a matter of the detailed arrangement of all coordinates at once, which is why a generator must learn the sheet rather than interpolate its examples.