Artificial Intelligence and Computer Vision for Cultural Heritage
M2 MVA · Semester 2 · Opening · 2.5 ECTS
Contents
- Introduction and overview
- 0. Review: images, filters, Fourier analysis, least squares and probability
- 1. Technical and chemical imaging of paintings
- 2. Cross-modal image registration
- 3. Conditional expectation in multimodal imaging
- 4. Microstructure: craquelure and canvas
- 5. Text and image embeddings with pretrained networks
- 6. Image-to-image translation with CNNs
- 7. Complements: optics of paint, information and uncertainty
- 8. Research directions and open problems
- Exercises
- Projects and assessment
- References
Introduction and overview
| Module | Artificial Intelligence and Computer Vision for Cultural Heritage |
| Official title | Artificial Intelligence and Computer Vision for Cultural Heritage |
| Instructors | R. ERDMANN (master-mva.com, 2026–2027) |
| Period | Semester 2 · period 2 (04/01/2027 – 02/04/2027) |
| Credits and volume | 2.5 ECTS · 12 h (lectures 12 h) per the Université Paris-Saclay programme table |
| Label | Opening · domains on master-mva.com: Computer Vision |
| Language | not displayed on the official pages; the course description on master-mva.com is written in English |
| Assessment | projects submitted as code, documented and demonstrated in Jupyter notebooks (master-mva.com: “Evaluation will occur via projects submitted via code and documented and demonstrated via Jupyter notebooks.”) · Université Paris-Saclay: “Evaluation Continue non Intégrale” (continuous assessment that is not the whole grade) · no weights displayed |
| Prerequisites | Python with NumPy proficiency is required; some familiarity with SciPy, OpenCV, scikit-image, pandas and one of the major neural-network frameworks (PyTorch, TensorFlow, JAX) is recommended; Jupyter is used for interactive teaching and for evaluation (master-mva.com) |
Audience: a student with a good M1 in mathematics, computer science or physics who knows the basics of image processing and neural networks, and wants to apply them to paintings and drawings, where every image has a physical meaning.
Empirical first: photograph a painting in visible light and in the near infrared, and the two images disagree. In the infrared the paint becomes partly transparent and the artist’s underdrawing appears (ch. 1). Overlay the two images and nothing matches to the pixel, because each camera has its own lens distortion. After a sub-pixel registration (ch. 2), the pixels whose infrared value the colour does not explain point to underdrawing, retouching or a change of mind (ch. 3).
Art history and conservation science now collect gigabytes, sometimes terabytes, of images of a single painting, in several physical modalities. This course teaches the imaging physics behind those images and the computer vision that turns them into evidence: registration, density estimation, segmentation of cracks and canvas weave, pretrained embeddings and image-to-image networks. The course is given by Robert G. Erdmann (University of Amsterdam), as listed on master-mva.com.
Related MVA pages: 3D Computer Vision for camera geometry, Sub-Pixel Image Processing for precise shifts, Remote Sensing Data for multispectral imaging at another scale, and Deep Learning for Image Restoration and Synthesis for image-to-image networks.
What you will be able to do
- Explain what visible photography, infrared reflectography, reflectance spectroscopy, radiography, macro X-ray fluorescence and structured-light scanning measure, and at which depth of a painting.
- Calibrate colour by least squares, unmix reflectance spectra, and recover a height map from phase-shifted fringes.
- Register two modalities: detect, describe and match keypoints, estimate a homography with lens distortion robustly, and refine local offsets to a fraction of a pixel.
- Choose or learn a similarity measure that works when the intensities of two modalities are not related by a monotone map.
- Estimate a low-dimensional density, compute surprisal and conditional surprisal maps, and interpret them.
- Train a CNN that predicts colour from element maps, and use its residuals to locate retouching.
- Segment craquelure with scale-normalised Hessian filters and describe a crack network by its orientation statistics.
- Measure thread densities from a radiograph and test whether two canvases were cut from the same roll.
- Build a text-and-image search engine with CLIP, use semantic directions, and find patch correspondences with DINOv2 features.
- Explain the U-Net and neural style transfer, prove that the Gram-matrix loss is a maximum mean discrepancy, and design an image-to-image network for a conservation question.
Official objectives and syllabus (2026–2027)
The course description on master-mva.com starts from a change in the field. New and inexpensive imaging modalities, fast computation and fast networks let researchers collect gigabytes or terabytes of multimodal images of one artwork. These data can improve what we know about who made it, how it was made, its state of conservation and its relation to other artworks.
The description adds that heritage institutions are digitising millions of works and making them freely available. Researchers can then study central questions of art history, such as attribution, style and iconography, in new ways. Extracting this value requires sophisticated computational tools.
The aims are two-fold: to introduce the rich and little-known world of cultural heritage imaging and its problems, and to introduce a selection of the most important recent developments in computer vision and deep learning that solve them. The Université Paris-Saclay course detail lists the course as “AI and Computer vision for cultural heritage” and gives only its form of assessment.
| Official topic (master-mva.com, “Thèmes abordés” and organisation, 2026–2027) | Course section |
|---|---|
| 1. Overview of technical and chemical imaging; relation to materiality | ch. 1: the layered painting (Definition 1.1) and the table of what each modality measures |
| 1a. High-resolution visible photography (VIS) | ch. 1.1: colour calibration (Proposition 1.2), resolution and data volume (Example 1.3) |
| 1b. Infrared reflectography (IRR) | ch. 1.2: contrast of an underdrawing (Proposition 1.4); the Kubelka–Munk model in ch. 7.1 |
| 1c. Reflectance imaging spectroscopy (RIS) | ch. 1.3: spectral cubes, linear mixing (Definition 1.5), non-negative unmixing and spectral angle; non-linear mixing in ch. 7.1 |
| 1d. Radiography | ch. 1.4: Beer–Lambert law (Proposition 1.6), why lead white dominates the image |
| 1e. Macro X-ray fluorescence (MA-XRF) | ch. 1.5: Moseley’s law (Proposition 1.7), element maps and overlapping lines (Example 1.8) |
| 1f. Structured-light scanning | ch. 1.6: three-step phase shifting and height from phase (Proposition 1.9, Example 1.10) |
| 2. Cross-modal image registration | ch. 2: the three-stage pipeline |
| 2a. Deep networks for feature detection, description and matching: KeyNet, AffNet, HardNet | ch. 2.1–2.2: scale space and structure tensor (Definition 2.1), Key.Net, AffNet, the HardNet loss (Definition 2.2), ratio test and mutual matching |
| 2b. Robust parametric estimation of geometric transforms: homographies, lens distortion, MAGSAC++ | ch. 2.3–2.4: plane-induced homography (Proposition 2.3), DLT, radial distortion (Definition 2.4, Example 2.5), RANSAC (Proposition 2.6), σ-marginalised weights of MAGSAC++ (Proposition 2.7) |
| 2c. Learning nonlinear filters for precise patchwise offset estimation: scale space theory, structure tensor, similarity metric design, parallel tile-stack cross-correlation | ch. 2.5: aperture problem (Proposition 2.8), normalised cross-correlation (Proposition 2.9), phase correlation and tile stacks (Proposition 2.10), learned filters (Definition 2.11); mutual information in ch. 7.2 |
| 3. Modelling conditional expectation in multimodal imaging of oil paintings | ch. 3, with Theorem 0.7 (conditional expectation as best predictor) |
| 3a. Density estimation for low-dimensional data (R, G, B, IRR); surprisal (examples from Hieronymus Bosch) | ch. 3.1: kernel density estimation (Theorem 3.2), surprisal and conditional surprisal (Definition 3.3, Proposition 3.4) |
| 3b. CNNs to predict colour from chemistry (VIS from MA-XRF); detection of restoration (examples from Rembrandt) | ch. 3.2: Definition 3.5 and the residual test |
| 4. Direct microstructural analysis of paintings | ch. 4 |
| 4a. Crack type classification; craquelure segmentation and analysis from visible photography (examples from Van Eyck) | ch. 4.1: crack types (Definition 4.1), scale selection for lines (Proposition 4.2), orientation statistics (Definition 4.3) |
| 4b. Canvas analysis from radiography; reuniting paintings cut from the same roll (Poussin, Van Gogh, Rembrandt, Frans Hals) | ch. 4.2: thread density (Definition 4.4), warp matching (Proposition 4.5), significance (Example 4.6) |
| 5. Text and image embeddings with pre-trained networks | ch. 5 |
| 5a. Joint image–text embedding with CLIP | ch. 5.1: Definition 5.1, the duplicate-caption floor (Proposition 5.2); the information bound in ch. 7.3 |
| 5a-i. Cultural heritage image search engine | ch. 5.2 |
| 5a-ii. Semantic directions in the embedding space | ch. 5.2: Definition 5.3 |
| 5a-iii. High-resolution microstructural image comparison | ch. 5.3 |
| 5b. Self-supervised embeddings with DINOv2 | ch. 5.4 |
| 5b-i. Semantic correspondences between artworks via local patch embeddings | ch. 5.4: mutual nearest neighbours (Definition 5.4) |
| 5b-ii. Visualisation via PCA | ch. 5.5 |
| 6. Image-to-image translation with CNNs | ch. 6 |
| 6a. U-Net architecture | ch. 6.1: Proposition 6.1 (receptive field and tiling) |
| 6b. VGG16, Gram matrix, principles of style transfer | ch. 6.2: Definition 6.2, Proposition 6.3 (Gram loss as MMD) |
| 6c. Case study: the Magic Eraser (ink removal to detect watermarks in works on paper) | ch. 6.3 |
| 6d. Case study: reconstructing the missing parts of Rembrandt’s Night Watch | ch. 6.4; uncertainty of reconstructions in ch. 7.4 |
| Aim: who made a work, how, its state of conservation, its relation to other artworks | ch. 1 and 3 (how it was made), ch. 3.2 and 4.1 (conservation), ch. 4.2 and 5 (relations between works) |
| Aim: millions of digitised works; attribution, style and iconography | ch. 5 (search, semantic directions), ch. 6.2 (style), ch. 8 (attribution) |
| Organisation: four 3-hour lectures | the session plan |
| Organisation: hands-on work with real data from the instructor’s 19 years in cultural heritage imaging | not covered: these data are not public; every code block uses a synthetic stand-in, and the projects use open-access images |
| Course readings: KeyNet, MAGSAC++, Neural Style Transfer, U-Net, CLIP, DINOv2 | ch. 2.1, 2.4, 6.2, 6.1, 5.1 and 5.4; the papers lead the References |
| Prerequisites: Python and NumPy; SciPy, OpenCV, scikit-image, pandas, a deep-learning framework; Jupyter | ch. 0 and the code blocks (NumPy, SciPy, scikit-learn, PyTorch) |
| Assessment: projects as code with Jupyter notebooks; Université Paris-Saclay: “Evaluation Continue non Intégrale” | Projects and assessment |
| Additional reading: Tummers (2011), The eye of the connoisseur | References; attribution and connoisseurship in ch. 8 |
Every official topic is taught on this page, except the instructor’s own data sets. The extensions beyond the syllabus are the review chapter 0 and the complements of chapter 7: the Kubelka–Munk optics of paint, mutual information, the information bound of contrastive learning and distribution-free uncertainty for reconstructions.
Where this course fits in the programme
Official prerequisites: Python with NumPy (required), and some familiarity with SciPy, OpenCV, scikit-image, pandas and a deep-learning framework (recommended). It is an Opening course of semester 2 (period 2), with 2.5 ECTS and 12 h of lectures.
- Preparation — the refreshers Scientific Python, NumPy and PyTorch (every code block), Linear Algebra, Analysis and Optimization (SVD, least squares) and Probability and Statistics (densities, conditional expectation).
- Semester 1 courses that prepare it — 3D Computer Vision (homographies, calibration), Object Recognition and Computer Vision (local features, CNNs), Deep Learning, Sub-Pixel Image Processing, Mathematical Models for Digital Photography (colour, lenses) and Introduction to Statistical Learning.
- Related courses of semester 2 — Remote Sensing Data (spectral imaging, registration), Point Clouds and 3D Modelling (scanning), Multimodal Explainable AI (CLIP), Representation Learning for Computer Vision and Medical Imaging (self-supervision), Deep Learning for Image Restoration and Synthesis, Generative Models for Images and Kernel Methods for Machine Learning (MMD).
- Afterwards — a heritage-imaging question makes a natural Research Internship.
Course outline
- Review: images, filters, Fourier analysis, least squares and probability — the M1 tools used later, in the form used later (self-study).
- Technical and chemical imaging of paintings — what each of six modalities measures, at which depth, with the physics that links images to materials.
- Cross-modal image registration — keypoints and learned descriptors, homographies and lens distortion, robust estimation, sub-pixel patchwise offsets.
- Conditional expectation in multimodal imaging — densities and surprisal of pixel vectors, and a CNN that predicts colour from chemistry.
- Microstructure: craquelure and canvas — crack segmentation and statistics, thread counting and canvas matching from radiographs.
- Text and image embeddings with pretrained networks — CLIP for search and semantic directions, DINOv2 for correspondences and PCA maps.
- Image-to-image translation with CNNs — the U-Net, Gram matrices and style transfer, and two case studies: ink removal and the Night Watch.
- Complements: optics of paint, information and uncertainty — Kubelka–Munk, mutual information, the InfoNCE bound, conformal intervals (self-study).
- Research directions and open problems — where the field is going and what is still unsolved (self-study).
Session plan
12 h in class = 4 sessions of 3 h. The volume is the Université Paris-Saclay programme table (12 h of lectures, 2.5 ECTS), and it matches the organisation announced on master-mva.com: four 3-hour lectures, each with a considerable part of hands-on work on real data. The split of the six official themes between the four sessions is a proposal of these notes.
Level 1 = foundations (the physics of the modalities, first models), 2 = core methods, 3 = advanced methods and case studies; the level never decreases.
| Session | Hours | Level | Content | Practice |
|---|---|---|---|---|
| 1 | 3 h | 1 | ch. 1 Technical and chemical imaging: VIS, IRR, RIS, radiography, MA-XRF, structured light; ch. 2.1–2.2 Keypoints, Key.Net, AffNet, HardNet, matching | Ex. 1, 2, 3; code of ch. 1 |
| 2 | 3 h | 2 | ch. 2.3–2.5 Homographies, lens distortion, RANSAC and MAGSAC++, patchwise offsets and tile stacks; ch. 3 Density, surprisal, colour from chemistry | Ex. 4, 5, 10, 11, 12; code of ch. 2 and 3 |
| 3 | 3 h | 2 | ch. 4 Craquelure and canvas weave; ch. 5.1–5.3 CLIP, search engine, semantic directions, high-resolution comparison | Ex. 6, 7, 13, 14, 15 |
| 4 | 3 h | 3 | ch. 5.4–5.5 DINOv2 correspondences and PCA; ch. 6 U-Net, style transfer, the Magic Eraser, the Night Watch | Ex. 8, 9, 16; Ex. 19 as a mini-project |
Self-study, outside the 12 h (about 7 h in total): ch. 0 Review, to read before session 1 (≈ 2 h) · ch. 7 Complements: Kubelka–Munk, mutual information, InfoNCE, conformal intervals, after session 4 (≈ 3 h, with Ex. 17) · ch. 8 Research directions and open problems (≈ 2 h, with Ex. 18). The project comes on top (see Projects and assessment).
How to study this module
- Read ch. 0 before session 1, even quickly: chapters 2, 3 and 6 use its theorems without proving them again.
- Before each session, read the chapters of its row; after it, do the exercises of its row. ★ applies a definition, ★★ asks for a short proof or a computation, ★★★ is open-ended.
- Run every code block in a Jupyter notebook, the format of the official assessment. They use NumPy, SciPy, scikit-learn and PyTorch on a CPU, with fixed seeds, and take under 30 seconds each.
- The code works on synthetic stand-ins for museum data. For each one, write down which physical assumption the simulation makes, and how a real image could break it.
- Read the official papers in the order of the sessions: Key.Net, HardNet and AffNet, then MAGSAC++ (sessions 1–2); CLIP (session 3); DINOv2, U-Net and Gatys et al. (session 4).
- Keep Tummers (2011) for the end: it shows how connoisseurs weigh evidence, which is what the computational methods must feed.
0. Review: images, filters, Fourier analysis, least squares and probability
The course combines the physics of imaging, classical image processing and deep learning. This review collects the M1-level tools that later chapters use without proof: linear light, convolution, the Fourier transform, the singular value decomposition, conditional expectation and the vocabulary of convolutional networks.
0.1 Images and linear light
Definition 0.1 — Digital image, pixel pitch A digital image with c channels is an array I : \{0, \dots, H-1\} \times \{0, \dots, W-1\} \to \mathbb{R}^{c}. Its pixel pitch \Delta is the physical size of one pixel on the object, in millimetres or micrometres.
Physical models, such as the attenuation of light by a layer, act on quantities proportional to energy. Camera files are usually gamma-encoded: a stored sRGB value C \in [0, 1] is decoded to linear light by C_{\mathrm{lin}} = \begin{cases} C / 12.92, & C \le 0.04045, \\ \big((C + 0.055)/1.055\big)^{2.4}, & C > 0.04045. \end{cases} Every model of ch. 1 and ch. 3 assumes linear values.
0.2 Convolution, correlation and Gaussian smoothing
Definition 0.2 — Convolution and cross-correlation For images f, g on \mathbb{Z}^2, (f * g)(x) = \sum_{y} f(y)\, g(x - y), \qquad (f \star g)(t) = \sum_{x} f(x)\, g(x + t). The Gaussian kernel of scale \sigma is G_\sigma(x) = (2\pi\sigma^2)^{-1}\, e^{-|x|^2 / (2\sigma^2)} .
Three facts are used constantly.
- The Gaussian is separable: a 2-D smoothing is two 1-D smoothings, one per axis.
- Smoothings compose: G_\sigma * G_\tau = G_{\sqrt{\sigma^2 + \tau^2}}.
- Derivatives commute with convolution, so a smoothed derivative is a convolution with a derivative of the Gaussian: \partial_i (G_\sigma * I) = (\partial_i G_\sigma) * I .
0.3 The discrete Fourier transform
Definition 0.3 — DFT For a signal f of length N, the discrete Fourier transform is \hat f(k) = \sum_{x=0}^{N-1} f(x)\, e^{-2\pi i k x / N}, \qquad k = 0, \dots, N - 1 . The fast Fourier transform computes it in O(N \log N) operations; in 2-D it acts on each axis in turn.
Theorem 0.4 — Convolution and shift theorems With indices taken modulo N (circular convolution and shifts): \widehat{f * g} = \hat f\, \hat g, \qquad \widehat{f(\cdot - t)}(k) = e^{-2\pi i k t / N}\, \hat f(k).
Proof.
- Shift: substitute y = x - t in the sum defining the transform of f(\cdot - t); the factor e^{-2\pi i k t/N} comes out of the sum.
- Convolution: write \widehat{f*g}(k) = \sum_y f(y) \sum_x g(x - y)\, e^{-2\pi i k x/N} . By the shift identity, the inner sum is e^{-2\pi i k y / N}\, \hat g(k), and the outer sum gives \hat f(k). ∎
The shift theorem is the basis of phase correlation in ch. 2.5, and the convolution theorem makes large correlations cheap.
0.4 Least squares and the SVD
Proposition 0.5 — Homogeneous least squares Let A \in \mathbb{R}^{m \times n}, with m \ge n, have singular values \sigma_1 \ge \dots \ge \sigma_n and right singular vectors v_1, \dots, v_n. Then \min_{\|x\| = 1} \|A x\| = \sigma_n, \quad \text{attained at } x = v_n .
Proof. Write A = U \Sigma V^\top and y = V^\top x, so that \|y\| = 1. Then \|Ax\|^2 = \sum_i \sigma_i^2 y_i^2 \ge \sigma_n^2 \sum_i y_i^2 = \sigma_n^2 , with equality for y = e_n, that is x = v_n. ∎
The ordinary problem \min_x \|Ax - b\|^2 with A of full column rank has the solution of the normal equations, x^\star = (A^\top A)^{-1} A^\top b . Homography estimation (ch. 2.3) uses the first form, colour calibration (ch. 1.1) the second.
0.5 Densities, information and conditional expectation
Definition 0.6 — Entropy, cross-entropy, Kullback–Leibler divergence For densities (or probability mass functions) p and q: H(p) = -\mathbb{E}_p[\log p], \qquad H(p, q) = -\mathbb{E}_p[\log q], \qquad \mathrm{KL}(p \,\|\, q) = H(p, q) - H(p) \ge 0 . The quantity -\log p(x) is the surprisal of the outcome x; the entropy is its mean.
Theorem 0.7 — Conditional expectation is the best predictor Let Y be square-integrable and m(X) = \mathbb{E}[Y \mid X]. For every measurable g with g(X) square-integrable, \mathbb{E}\big[(Y - g(X))^2\big] = \mathbb{E}\big[(Y - m(X))^2\big] + \mathbb{E}\big[(m(X) - g(X))^2\big]. So m minimises the mean squared error, and any model trained with a squared loss estimates it.
Proof.
- Expand Y - g(X) = (Y - m(X)) + (m(X) - g(X)) and square.
- By the tower property, the cross term is 2\,\mathbb{E}\big[(m(X) - g(X))\, \mathbb{E}[Y - m(X) \mid X]\big] .
- The inner conditional expectation is zero by definition of m. ∎
0.6 The toolbox of convolutional networks
- A convolutional layer applies learned k \times k filters to all input channels, adds a bias and a non-linearity such as ReLU, \max(0, u). Pooling or a stride s > 1 reduces the resolution.
- The receptive field of a unit is the input region it depends on. For layers with kernel sizes k_l and strides s_l, it is r_L = 1 + \sum_{l=1}^{L} (k_l - 1) \prod_{j < l} s_j .
- A softmax with temperature \tau turns scores z_j into probabilities e^{z_j/\tau} / \sum_k e^{z_k/\tau}. The cosine similarity of two vectors is their inner product after normalisation to unit length.
- Training minimises an average loss by stochastic gradient methods such as Adam; automatic differentiation computes the gradients.
The code below checks Theorem 0.4 and Proposition 0.5 numerically.
import numpy as np
rng = np.random.default_rng(0)
n = 64
f, g = rng.standard_normal(n), rng.standard_normal(n)
# Convolution theorem (circular, 1-D): DFT(f * g) = DFT(f) DFT(g)
circ = np.array([sum(f[m] * g[(k - m) % n] for m in range(n)) for k in range(n)])
err = np.abs(np.fft.fft(circ) - np.fft.fft(f) * np.fft.fft(g)).max()
print(f"convolution theorem, max error: {err:.1e}")
# Shift theorem: a circular shift by t multiplies the DFT by exp(-2 i pi k t / n)
t, k = 5, np.arange(n)
lhs = np.fft.fft(np.roll(f, t))
rhs = np.exp(-2j * np.pi * k * t / n) * np.fft.fft(f)
print(f"shift theorem, max error: {np.abs(lhs - rhs).max():.1e}")
# Homogeneous least squares: argmin ||A x|| over unit x = last right singular vector
A = rng.standard_normal((20, 9))
x_true = rng.standard_normal(9)
x_true /= np.linalg.norm(x_true)
A -= np.outer(A @ x_true, x_true) # x_true is now an exact null vector of A
_, s, Vt = np.linalg.svd(A)
print(f"smallest singular value: {s[-1]:.1e}, |<v_9, x_true>| = {abs(Vt[-1] @ x_true):.6f}")Key takeaways — chapter 0
- Physical models act on linear light; decode gamma-encoded files first.
- Gaussian smoothing is separable and composes; smoothed derivatives are convolutions with derivatives of the Gaussian.
- A shift multiplies the Fourier transform by a phase; convolution becomes a product.
- The unit vector minimising \|Ax\| is the last right singular vector of A.
- The conditional expectation is the best predictor in mean square; a network trained with a squared loss estimates it.
1. Technical and chemical imaging of paintings
A painting is a layered object, and each imaging modality measures one physical quantity at one depth.
Definition 1.1 — Layered painting, multimodal stack A painting is a stack of layers: a support (wood panel or canvas), a ground, an optional underdrawing, paint layers and varnish. A multimodal stack of the painting is a family of images I_m : \Omega_m \to \mathbb{R}^{c_m}, \qquad m = 1, \dots, M, each on its own pixel grid \Omega_m.
| Modality | Physical quantity | Depth reached | Typical use |
|---|---|---|---|
| Visible photography (VIS) | reflectance, 400–700 nm | surface | colour, condition, cracks |
| Infrared reflectography (IRR) | reflectance, 0.9–2.5 µm | below many paint layers | underdrawing |
| Reflectance imaging spectroscopy (RIS) | a spectrum per pixel | as VIS and IRR | pigments |
| Radiography | X-ray transmission | whole object | lead white, weave, wood |
| Macro X-ray fluorescence (MA-XRF) | element fluorescence | paint layers | element maps |
| Structured-light scanning | surface height | surface | relief, deformation |
1.1 Visible photography
A colour chart with n patches of known linear values y_k \in \mathbb{R}^3 is photographed with the painting, giving camera values x_k \in \mathbb{R}^3.
Proposition 1.2 — Colour calibration by least squares Let X, Y \in \mathbb{R}^{3 \times n} hold the x_k and y_k as columns, with X X^\top invertible. The 3 \times 3 matrix minimising \sum_k \|y_k - M x_k\|^2 is M^\star = Y X^\top (X X^\top)^{-1}.
Proof. Each row of M solves an ordinary least-squares problem with design matrix X^\top; the normal equations of ch. 0.4, stacked over the three rows, give the formula. ∎
Example 1.3 — Data volume A painting of 1 \times 1 m photographed at a pixel pitch of 10 µm has 10^5 \times 10^5 = 10^{10} pixels. With three channels of 16 bits: 10^{10} \times 3 \times 2 \text{ bytes} = 60 \text{ GB}. Such images are mosaics of many shots, which must be registered (ch. 2).
1.2 Infrared reflectography
Paint scatters less, and many pigments absorb less, at longer wavelengths, so infrared light crosses layers that are opaque in the visible. Carbon-based drawing materials absorb at all wavelengths.
Proposition 1.4 — Contrast of an underdrawing Let a paint layer have reflectance R_p(\lambda) and one-pass transmittance T(\lambda), above either the ground (reflectance R_g) or an underdrawing line (R_u < R_g). Neglecting multiple reflections between the layers, the contrast between the two places is \Delta R(\lambda) = T(\lambda)^2 \,(R_g - R_u).
Proof. Light reflected below the paint crosses it twice, so R \approx R_p + T^2 R_{\text{below}}; subtract the two cases. ∎
The contrast grows with T(\lambda), hence with the wavelength.
1.3 Reflectance imaging spectroscopy
A RIS camera records a cube r(x, \lambda) of often hundreds of bands, which identify pigments far better than three colour channels.
Definition 1.5 — Linear mixing model With p reference spectra (endmembers) as the columns of E \in \mathbb{R}^{B \times p}, the spectrum of a pixel is modelled as r = E a + \varepsilon, \qquad a \ge 0, \quad \mathbf{1}^\top a = 1 . The abundances a are estimated by non-negative least squares. The spectral angle \arccos\big(\langle r, e\rangle / (\|r\|\,\|e\|)\big) compares shapes, independently of illumination.
It holds for side-by-side areas of pigment, not for pigments mixed in one layer.
import numpy as np
from scipy.optimize import nnls
# Moseley's law for K-alpha lines: E = (3/4) * 13.6 eV * (Z - 1)^2
for name, Z, tabulated in [("Ca", 20, 3.69), ("Fe", 26, 6.40), ("Cu", 29, 8.05)]:
print(f"{name}: Moseley {0.75 * 13.6e-3 * (Z - 1) ** 2:5.2f} keV, tabulated {tabulated:5.2f} keV")
# Linear unmixing of a synthetic reflectance spectrum (400-1000 nm, 121 bands)
lam = np.linspace(400, 1000, 121)
sig = lambda x, c, w: 1 / (1 + np.exp(-(x - c) / w))
E = np.stack([0.85 + 0.05 * sig(lam, 450, 30), # synthetic "white"
0.05 + 0.80 * sig(lam, 600, 12), # synthetic "red"
0.10 + 0.30 * np.exp(-((lam - 460) / 40) ** 2) # synthetic "blue"
+ 0.50 * sig(lam, 800, 40)], axis=1) # E has shape (121, 3)
a_true = np.array([0.2, 0.5, 0.3])
rng = np.random.default_rng(1)
r = E @ a_true + 0.01 * rng.standard_normal(lam.size)
# Non-negative least squares with a heavily weighted sum-to-one row
w = 100.0
a_hat, _ = nnls(np.vstack([E, w * np.ones(3)]), np.append(r, w))
print("abundances: true", a_true, " estimated", a_hat.round(3))
# Spectral angle between the measured spectrum and each endmember (radians)
sam = np.arccos(E.T @ r / (np.linalg.norm(E, axis=0) * np.linalg.norm(r)))
print("spectral angles:", sam.round(3))1.4 Radiography
Proposition 1.6 — Beer–Lambert law Along a ray crossing layers of linear attenuation coefficients \mu_k and thicknesses t_k, the transmitted intensity satisfies -\log \frac{I}{I_0} = \sum_k \mu_k\, t_k .
Lead white attenuates X-rays far more than binders, canvas or wood, so a radiograph of an old master is mostly a map of lead-white thickness. Where the ground fills the gaps between the threads, the canvas weave appears (ch. 4.2).
1.5 Macro X-ray fluorescence
An X-ray beam scanned over the painting makes each element emit lines at energies specific to it. One spectrum per position gives one map per element.
Proposition 1.7 — Moseley’s law For an element of atomic number Z, the K\alpha line has energy approximately E_{K\alpha} \approx \tfrac{3}{4} \times 13.6 \text{ eV} \times (Z - 1)^2 .
Proof. Sketch. In the Bohr model, the transition from shell 2 to shell 1 releases 13.6\,\text{eV}\,Z_{\text{eff}}^2 (1 - 1/4). The remaining K electron screens the nucleus, so Z_{\text{eff}} \approx Z - 1. ∎
Example 1.8 — Element maps and overlapping lines The law gives 6.37 keV for iron and 8.00 keV for copper (tabulated: 6.40 and 8.05 keV). Mercury suggests vermilion; copper, azurite, malachite or verdigris; iron, earth pigments.
The As K\alpha (10.54 keV) and Pb L\alpha (10.55 keV) lines coincide; As K\beta (11.73 keV) and Pb L\beta (12.61 keV) separate them.
Deep layers are seen through the layers above, which absorb low energies most, so element maps also carry depth information. Dik et al. (2008) used them to reveal a head hidden under a painting by Van Gogh.
1.6 Structured-light scanning
A projector casts sinusoidal fringes on the surface and a camera views them from another direction. Relief displaces the fringes.
Proposition 1.9 — Three-step phase shifting Let three images be I_j = A + B\cos(\varphi + \delta_j) with \delta_j = 0, 2\pi/3, 4\pi/3. Then \varphi = \operatorname{atan2}\big(\sqrt{3}\,(I_3 - I_2),\; 2 I_1 - I_2 - I_3\big). With fringes of period p on the reference plane, projected at angle \theta to the viewing direction, a height h shifts the phase by \Delta\varphi = 2\pi h \tan\theta / p .
Proof.
- Expanding the cosines gives I_3 - I_2 = \sqrt{3}\,B \sin\varphi and 2I_1 - I_2 - I_3 = 3B\cos\varphi; A and B cancel in the ratio.
- A height h moves the projected fringe sideways by h\tan\theta, a fraction h\tan\theta/p of a period. ∎
Example 1.10 — Height resolution With p = 2 mm, \theta = 30^\circ and a phase noise of 0.01 rad, the height noise is \frac{2 \times 0.01}{2\pi \tan 30^\circ} \approx 0.0055 \text{ mm} = 5.5\ \mu\text{m}, the order of magnitude found by the code below.
import numpy as np
# Three-step phase shifting on a synthetic impasto bump (units: mm)
x, y = np.meshgrid(np.linspace(-10, 10, 201), np.linspace(-10, 10, 201))
h = 0.5 * np.exp(-(x**2 + y**2) / 8) # height map, 0.5 mm peak
p, theta = 2.0, np.deg2rad(30) # fringe period, projection angle
phi0 = 2 * np.pi * x / p # phase on the flat reference plane
phi = phi0 + 2 * np.pi * h * np.tan(theta) / p # phase displaced by the relief
A, B = 0.5, 0.4
rng = np.random.default_rng(0)
I1, I2, I3 = (A + B * np.cos(phi + d) + 0.005 * rng.standard_normal(phi.shape)
for d in (0, 2 * np.pi / 3, 4 * np.pi / 3))
wrapped = np.arctan2(np.sqrt(3) * (I3 - I2), 2 * I1 - I2 - I3)
dphi = np.angle(np.exp(1j * (wrapped - phi0))) # phase difference, wrapped to (-pi, pi]
h_hat = dphi * p / (2 * np.pi * np.tan(theta))
print(f"max phase change {dphi.max():.3f} rad (below pi: no unwrapping needed)")
print(f"height RMS error: {1000 * np.sqrt(np.mean((h_hat - h) ** 2)):.1f} micrometres")Key takeaways — chapter 1
- Each modality measures one physical quantity at one depth.
- Infrared reveals carbon-based underdrawings: paint transmits more at long wavelengths.
- Radiographs map lead white (Beer–Lambert); MA-XRF maps elements by their line energies.
- Phase shifting turns three fringe images into a height map.
- One painting yields tens of gigabytes on different grids: registration comes next.
2. Cross-modal image registration
Registration maps each pixel of one image to the corresponding point of another. Across modalities intensities differ, so features must be designed or learned to agree. The pipeline has three stages: keypoint matches, a robust global transform, local sub-pixel offsets.
2.1 Keypoints: scale space and the structure tensor
Definition 2.1 — Scale space, structure tensor The scale space of an image I is L(\cdot\,; t) = G_{\sqrt t} * I; it solves the heat equation \partial_t L = \tfrac12 \Delta L, and L_\sigma = L(\cdot\,; \sigma^2). At derivative scale \sigma and integration scale \rho, the structure tensor is J_{\sigma,\rho} = G_\rho * \big(\nabla L_\sigma \, \nabla L_\sigma^\top\big), \qquad \lambda_1 \ge \lambda_2 \ge 0 \text{ its eigenvalues}.
Two small eigenvalues mean a flat region, one large eigenvalue an edge, two large ones a corner. The Harris detector keeps local maxima of \det J - \kappa\,(\operatorname{tr} J)^2, with \kappa \approx 0.05.
Key.Net (Barroso-Laguna et al., 2019) combines such handcrafted derivative filters with learned filters in a multi-scale pyramid, trained on pairs related by random synthetic transforms.
2.2 Descriptors and matching
AffNet (Mishkin et al., 2018) estimates the affine shape of each keypoint region, trained to make descriptors discriminative rather than merely repeatable. HardNet (Mishchuk et al., 2017) maps a 32 \times 32 patch to a unit vector in dimension 128.
Definition 2.2 — HardNet loss For a batch of n matching pairs (a_i, p_i) of descriptors, let d_{ij} = \|a_i - p_j\|. The loss uses the hardest negative of each pair: \mathcal{L} = \frac{1}{n} \sum_{i=1}^{n} \max\Big(0,\; 1 + d_{ii} - \min_{j \ne i} \min(d_{ij}, d_{ji})\Big).
A nearest-neighbour match is kept if it is mutual and if the ratio of the first to the second distance is below about 0.8 (Lowe, 2004). Descriptors trained on photographs transfer well between VIS and IRR, which share many edges, less well to element maps.
2.3 Homographies and lens distortion
Proposition 2.3 — A plane induces a homography Let two pinhole cameras be K[I \mid 0] and K'[R \mid t], and let the scene lie on the plane n^\top X = d. The images x and x' of a point, in homogeneous coordinates, satisfy x' \sim H x with H = K' \big(R + t\, n^\top / d\big) K^{-1}.
Proof.
- The point is X = s K^{-1} x for some s > 0, and n^\top X / d = 1 on the plane.
- In the second camera, RX + t = RX + t\,(n^\top X)/d = (R + t n^\top/d)\, X.
- Project with K' and substitute X; the scalar s disappears up to scale. ∎
A painting is nearly planar, so its images are related by homographies. Each match gives two linear equations in the entries of H; with four or more, the direct linear transform (DLT) solves A h = 0 by Proposition 0.5, after normalising the coordinates (Hartley, 1997).
Definition 2.4 — Radial lens distortion With c the distortion centre, f the focal length in pixels and r = \|x_u - c\| / f, an ideal point x_u is imaged at x_d = c + (x_u - c)\,(1 + k_1 r^2 + k_2 r^4).
Example 2.5 — Distortion is not a detail For a 6000 \times 4000 sensor with f = 8000 px, a corner lies at r = 3606/8000 \approx 0.45. With k_1 = -0.02, it moves by 0.02 \times 0.45^3 \times 8000 \approx 15 \text{ px}, far above sub-pixel accuracy. The coefficients are estimated with the homography by non-linear least squares, or with a target (Zhang, 2000).
2.4 Robust estimation: RANSAC and MAGSAC++
Many matches between modalities are wrong. RANSAC (Fischler and Bolles, 1981) fits the model to random minimal samples and keeps the one with the most inliers below a threshold.
Proposition 2.6 — Number of RANSAC draws If a fraction w of the matches are inliers and a minimal sample has size s, then N draws contain an all-inlier sample with probability at least p as soon as N \ge \frac{\log(1 - p)}{\log(1 - w^s)} .
Proof. Each draw is all-inlier with probability w^s, independently; all N draws fail with probability (1 - w^s)^N \le 1 - p. ∎
For w = 0.4, s = 4 and p = 0.99: 178 draws. The fixed threshold is the weak point; MAGSAC++ (Barath et al., 2020) marginalises over the noise scale \sigma instead, and refits by iteratively reweighted least squares.
Proposition 2.7 — A σ-marginalised weight (simplified MAGSAC++ weight) Suppose that the residual norm r of an inlier is Rayleigh distributed given \sigma, p(r \mid \sigma) = (r/\sigma^2)\, e^{-r^2/(2\sigma^2)}, and that \sigma is uniform on (0, \sigma_{\max}]. Then p(r) = \frac{1}{\sigma_{\max}} \sqrt{\frac{\pi}{2}}\; \operatorname{erfc}\!\Big(\frac{r}{\sqrt{2}\,\sigma_{\max}}\Big).
Proof.
- Write p(r) as the average of p(r \mid \sigma) over \sigma \in (0, \sigma_{\max}], and substitute u = r^2/(2\sigma^2).
- The integrand becomes 2^{-1/2}\, u^{-1/2} e^{-u}\, du, on u \ge u_0 = r^2/(2\sigma_{\max}^2).
- Finally, \int_{u_0}^\infty u^{-1/2} e^{-u}\, du = \sqrt{\pi}\, \operatorname{erfc}(\sqrt{u_0}) . This gives the formula. ∎
The weight decreases smoothly, without a threshold; MAGSAC++ applies this construction to each model’s residual distribution, with incomplete gamma functions.
import numpy as np
from scipy.special import erfc
def normalise(x):
"""Similarity T sending the points to zero mean and mean distance sqrt(2)."""
c = x.mean(0)
s = np.sqrt(2) / np.linalg.norm(x - c, axis=1).mean()
return np.array([[s, 0, -s * c[0]], [0, s, -s * c[1]], [0, 0, 1]])
def dlt(x, y, w=None):
"""Weighted normalised DLT: homography H with y ~ H x."""
w = np.ones(len(x)) if w is None else w
Tx, Ty = normalise(x), normalise(y)
xh = np.c_[x, np.ones(len(x))] @ Tx.T
yh = np.c_[y, np.ones(len(y))] @ Ty.T
z = np.zeros_like(xh)
A = np.r_[np.c_[z, -xh, yh[:, 1:2] * xh], np.c_[xh, z, -yh[:, 0:1] * xh]]
A *= np.sqrt(np.r_[w, w])[:, None]
H = np.linalg.svd(A)[2][-1].reshape(3, 3) # Proposition 0.5
H = np.linalg.inv(Ty) @ H @ Tx
return H / H[2, 2]
def apply(H, x):
yh = np.c_[x, np.ones(len(x))] @ H.T
return yh[:, :2] / yh[:, 2:]
rng = np.random.default_rng(0)
H_true = np.array([[1.02, 0.03, 15.0], [-0.02, 0.98, -8.0], [2e-5, -1e-5, 1.0]])
n, w_in = 300, 0.4
x = rng.uniform(0, 1000, (n, 2))
y = apply(H_true, x) + 0.5 * rng.standard_normal((n, 2)) # inliers: 0.5 px noise
out = rng.random(n) > w_in
y[out] = rng.uniform(0, 1000, (out.sum(), 2)) # 60 % gross outliers
# RANSAC with the number of draws of Proposition 2.6
N = int(np.ceil(np.log(1 - 0.99) / np.log(1 - w_in**4)))
best, thr = None, 3.0
for _ in range(N):
idx = rng.choice(n, 4, replace=False)
H = dlt(x[idx], y[idx])
inl = np.linalg.norm(apply(H, x) - y, axis=1) < thr
if best is None or inl.sum() > best.sum():
best = inl
H_ransac = dlt(x[best], y[best])
# Iteratively reweighted DLT with the sigma-marginalised weight of Proposition 2.7
H_irls = H_ransac
for _ in range(10):
r = np.linalg.norm(apply(H_irls, x) - y, axis=1)
H_irls = dlt(x, y, erfc(r / (np.sqrt(2) * thr)))
grid = np.stack(np.meshgrid(np.linspace(0, 1000, 11), np.linspace(0, 1000, 11)), -1).reshape(-1, 2)
for name, H in [("all points", dlt(x, y)), ("RANSAC + refit", H_ransac), ("sigma-weighted IRLS", H_irls)]:
err = np.linalg.norm(apply(H, grid) - apply(H_true, grid), axis=1)
print(f"{name:20s}: mean error {err.mean():8.3f} px, max {err.max():8.3f} px")
print(f"RANSAC draws: {N}, inliers kept: {best.sum()} of {(~out).sum()} true inliers")2.5 Patchwise offsets to sub-pixel precision
After the global transform, local offsets of a few pixels remain (scanner drift, deformed supports, XRF rasters). An offset is estimated per tile, then interpolated into a smooth field.
Proposition 2.8 — Aperture problem Let B(x) = A(x - \delta) + \text{noise} on a tile, with \delta small. Linearising A(x - \delta) \approx A(x) - \nabla A(x)^\top \delta, the least-squares offset solves J \delta = -\textstyle\sum_x \nabla A(x)\, \big(B(x) - A(x)\big), \qquad J = \sum_x \nabla A(x) \nabla A(x)^\top . The offset is determined if and only if the structure tensor J is invertible. With white noise of variance s^2, its covariance is about s^2 J^{-1}.
On a single straight edge \lambda_2 = 0: only the offset across the edge is measured. Tiles are weighted by their smallest eigenvalue.
Proposition 2.9 — Normalised cross-correlation For tiles a and b with means \bar a, \bar b, \mathrm{NCC}(a, b) = \frac{\langle a - \bar a,\, b - \bar b \rangle}{\|a - \bar a\|\,\|b - \bar b\|} \in [-1, 1], and, for every \alpha \ne 0, \mathrm{NCC}(\alpha a + \beta, b) = \operatorname{sign}(\alpha)\, \mathrm{NCC}(a, b) .
NCC tolerates gain, offset and sign changes, not a non-monotone relation between modalities. The remedy is a non-linear filter per modality, such as a gradient magnitude, mapping both images to a common representation.
Proposition 2.10 — Phase correlation If b(x) = a(x - t) circularly on an N \times N tile, the normalised cross-power spectrum (defined where \hat a \ne 0) is a pure phase, whose inverse DFT is a Kronecker delta at t: \frac{\hat b\, \overline{\hat a}}{|\hat b\, \overline{\hat a}|}(k) = e^{-2\pi i \langle k, t\rangle / N}.
Proof. By Theorem 0.4, \hat b = e^{-2\pi i \langle k, t \rangle/N}\, \hat a, and \hat a\, \overline{\hat a} = |\hat a|^2 cancels in the ratio. ∎
A parabola through the values c_{-1}, c_0, c_{+1} around the peak refines it on each axis: \delta = \frac{c_{-1} - c_{+1}}{2\,(c_{-1} - 2 c_0 + c_{+1})} . Stacking all tiles in one tensor lets a single batched FFT process them in parallel.
Definition 2.11 — Learned similarity filters Let f_\theta and g_\theta be small CNNs applied to the two modalities, and c_t the correlation of their outputs on two tiles, shifted by t. Given tiles with a known offset t^\star, the filters are trained with \mathcal{L}(\theta) = -\log \frac{e^{c_{t^\star}/\tau}}{\sum_t e^{c_t/\tau}} , so that the correlation surface becomes a sharp peak at the true offset.
import numpy as np
from scipy import ndimage as ndi
rng = np.random.default_rng(0)
n, T = 512, 64 # image size, tile size
A = ndi.gaussian_filter(rng.standard_normal((n, n)), 2.0)
A = (A - A.min()) / (A.max() - A.min()) # "modality 1" in [0, 1]
# Smooth displacement field of up to about 3 px, then a non-monotone change of contrast
gy, gx = np.mgrid[0:n, 0:n]
dy = 2.5 * np.sin(2 * np.pi * gx / n) + 0.3
dx = 2.0 * np.cos(2 * np.pi * gy / n) - 0.4
B = np.abs(ndi.map_coordinates(A, [gy - dy, gx - dx], order=3) - 0.5) # "modality 2"
def tiles(img):
"""Stack of non-overlapping T x T tiles, shape (number of tiles, T, T)."""
return img.reshape(n // T, T, n // T, T).swapaxes(1, 2).reshape(-1, T, T)
def phase_correlation(a, b):
"""Batched phase correlation (Proposition 2.10) with parabolic sub-pixel refinement."""
win = np.outer(np.hanning(T), np.hanning(T))
Fa = np.fft.fft2((a - a.mean((1, 2), keepdims=True)) * win)
Fb = np.fft.fft2((b - b.mean((1, 2), keepdims=True)) * win)
R = Fb * np.conj(Fa)
c = np.real(np.fft.ifft2(R / (np.abs(R) + 1e-12))) # one correlation surface per tile
i, j = np.unravel_index(c.reshape(len(c), -1).argmax(1), (T, T))
k = np.arange(len(c))
refine = lambda cm, c0, cp: 0.5 * (cm - cp) / (cm - 2 * c0 + cp)
di = refine(c[k, (i - 1) % T, j], c[k, i, j], c[k, (i + 1) % T, j])
dj = refine(c[k, i, (j - 1) % T], c[k, i, j], c[k, i, (j + 1) % T])
wrap = lambda q: (q + T // 2) % T - T // 2
return np.stack([wrap(i) + di, wrap(j) + dj], 1)
centres = np.arange(n // T) * T + T // 2
true = np.stack([dy[np.ix_(centres, centres)].ravel(), dx[np.ix_(centres, centres)].ravel()], 1)
grad = lambda img: ndi.gaussian_gradient_magnitude(img, 1.0) # a non-linear common representation
for name, a, b in [("raw intensities", A, B), ("gradient magnitude", grad(A), grad(B))]:
err = np.linalg.norm(phase_correlation(tiles(a), tiles(b)) - true, axis=1)
print(f"{name:19s}: median error {np.median(err):6.3f} px, tiles within 0.5 px: {np.mean(err < 0.5):.0%}")Here the second modality is |A - 0.5|. Raw correlation fails, while the gradient magnitude, unchanged by this map except on the level line A = 0.5, registers every tile.
Key takeaways — chapter 2
- Keypoints come from the structure tensor in scale space; Key.Net, AffNet and HardNet learn detection, shape and description.
- A plane is imaged through a homography (DLT from four matches); lens distortion must be modelled.
- RANSAC needs \log(1-p)/\log(1-w^s) draws; MAGSAC++ replaces its threshold by a σ-marginalised weight.
- Local offsets exist where the structure tensor is invertible; phase correlation of tile stacks estimates them.
- Across modalities, correlate non-linear features, not raw intensities.
3. Conditional expectation in multimodal imaging
Once registered, every pixel is a short vector of measurements, such as (R, G, B, IRR). The question “what is unusual here?” becomes a question about densities and conditional expectations.
3.1 Density estimation and surprisal
Definition 3.1 — Kernel density estimator For pixel vectors x_1, \dots, x_n \in \mathbb{R}^d, a kernel K and a bandwidth h > 0, \hat p_h(x) = \frac{1}{n h^d} \sum_{i=1}^{n} K\Big(\frac{x - x_i}{h}\Big).
Theorem 3.2 — Bias and variance of the estimator Let K be a symmetric density with \int u u^\top K = \mu_2 I and R(K) = \int K^2, and p twice differentiable. As h \to 0 and n h^d \to \infty, \mathbb{E}\,\hat p_h(x) - p(x) \approx \frac{h^2 \mu_2}{2}\, \Delta p(x), \qquad \operatorname{Var} \hat p_h(x) \approx \frac{p(x)\, R(K)}{n h^d}. The mean squared error is smallest for h \propto n^{-1/(d+4)}, where it decreases as n^{-4/(d+4)}.
Proof. Sketch.
- Substituting x_i = x - hu in the mean gives \mathbb{E}\,\hat p_h(x) = \int K(u)\, p(x - hu)\, du . A second-order Taylor expansion of p gives the bias; the first-order term vanishes by symmetry.
- The variance is n^{-1} times that of one term, dominated by \mathbb{E}[K_h(x - X)^2] \approx p(x) R(K)/h^d.
- Minimise c_1 h^4 + c_2/(n h^d) in h. ∎
With d = 4 and 10^8 pixels, the rate n^{-1/2} is excellent: densities of a few channels are easy to estimate, which is why the syllabus speaks of low-dimensional data.
Definition 3.3 — Surprisal and conditional surprisal The surprisal of a pixel vector is s(x) = -\log p(x). For a split x = (u, v), such as u = colour and v = infrared, the conditional surprisal is s(v \mid u) = -\log p(v \mid u) = s(u, v) - s(u).
For densities these values can be negative; only comparisons matter. A high s(u, v) flags a rare pixel, perhaps only a rare colour. A high s(v \mid u) flags an infrared value that the colour does not explain, such as an underdrawing or a change of composition. The syllabus illustrates surprisal maps on paintings by Hieronymus Bosch.
Proposition 3.4 — Gaussian case If (U, V) is jointly Gaussian with V scalar, let m(u) = \mathbb{E}[V \mid U = u] and s^2 = \operatorname{Var}(V \mid U). Then s(v \mid u) = \frac{(v - m(u))^2}{2 s^2} + \frac12 \log(2\pi s^2). Conditional surprisal is then a standardised squared residual of the best predictor of Theorem 0.7.
import numpy as np
from sklearn.neighbors import KernelDensity
from sklearn.metrics import roc_auc_score
rng = np.random.default_rng(0)
# Synthetic pixels (R, G, B, IRR): three common paints and one rare red paint
means = np.array([[0.85, 0.70, 0.60, 0.80], # light flesh tone, bright in infrared
[0.30, 0.35, 0.50, 0.55], # blue-grey
[0.20, 0.25, 0.15, 0.30], # dark brown
[0.75, 0.15, 0.10, 0.70]]) # rare red
counts = [1500, 1000, 800, 40]
X = np.vstack([m + 0.03 * rng.standard_normal((c, 4)) for m, c in zip(means, counts)])
kind = np.repeat(np.arange(4), counts)
under = (kind == 0) & (rng.random(len(X)) < 0.05) # carbon underdrawing below the flesh tone
X[under, 3] -= 0.25 # darker in IRR only, same colour
kde4 = KernelDensity(bandwidth=0.04).fit(X) # p(R, G, B, IRR)
kde3 = KernelDensity(bandwidth=0.04).fit(X[:, :3]) # p(R, G, B)
joint = -kde4.score_samples(X) # surprisal, in nats
cond = joint + kde3.score_samples(X[:, :3]) # -log p(IRR | RGB)
k = under.sum() # flag as many pixels as there are true ones
for name, s in [("joint", joint), ("conditional", cond)]:
top = np.argsort(s)[-k:]
print(f"{name:11s} surprisal: AUC {roc_auc_score(under, s):.3f}, among the {k} most surprising "
f"pixels {under[top].sum()} are underdrawing and {(kind[top] == 3).sum()} are the rare red")
# Nadaraya-Watson estimate of E[IRR | RGB] and a standardised residual
def nw(x_query, x, y, h=0.04):
d2 = ((x_query[:, None, :] - x[None, :, :]) ** 2).sum(-1)
w = np.exp(-d2 / (2 * h * h))
return (w * y).sum(1) / w.sum(1)
res = X[:, 3] - nw(X[:, :3], X[:, :3], X[:, 3])
z = res / res[~under].std()
print(f"residual z-score: underdrawing {z[under].mean():.1f}, other pixels {z[~under].mean():.2f}")Joint surprisal mixes the rare red with the underdrawing; conditional surprisal isolates the underdrawing.
3.2 Predicting colour from chemistry
Definition 3.5 — Colour predicted from element maps Let X \in \mathbb{R}^{c \times H \times W} be registered MA-XRF maps and Y \in \mathbb{R}^{3 \times H \times W} the calibrated photograph. A CNN f_\theta trained with the squared loss estimates, at each pixel x, \hat Y(x) = f_\theta(X)(x) \approx \mathbb{E}\big[Y(x) \mid X \text{ on a neighbourhood of } x\big], and the residual Y(x) - \hat Y(x) measures what chemistry does not explain.
A neighbourhood is needed rather than a single pixel. XRF maps are photon-limited and noisy, light diffuses inside the paint, and lines from deep layers are attenuated. Retouching made with a different colour over the same chemistry, or with materials absent from the input maps, leaves a large residual. The syllabus applies this to the detection of restorations in paintings by Rembrandt.
Train and test on separate blocks of the image: neighbouring pixels are correlated, and a random split of pixels overstates accuracy.
import numpy as np
import torch
from scipy import ndimage as ndi
from sklearn.metrics import roc_auc_score
torch.set_num_threads(1)
torch.manual_seed(0)
rng = np.random.default_rng(0)
S = 48
# Absorption of light (R, G, B) per unit concentration: Hg, Cu, Fe; Pb scatters and brightens
alpha = np.array([[0.1, 2.0, 2.0], # Hg: vermilion-like red
[2.0, 0.6, 0.4], # Cu: blue-green
[0.3, 0.8, 1.6]]) # Fe: earth, yellow-brown
def painting():
"""Element concentrations (Pb, Hg, Cu, Fe) and the visible colour they produce."""
f = ndi.gaussian_filter(rng.standard_normal((4, S, S)), (0, 4, 4))
c = np.log1p(np.exp(2 * f / f.std() - 0.5)) # smooth, non-negative maps
rgb = np.exp(-np.einsum("ehw,ec->chw", c[1:], alpha)) * (0.4 + 0.6 * np.tanh(c[0]))[None]
rgb = ndi.gaussian_filter(rgb, (0, 1.0, 1.0)) # light diffusion in the paint
xrf = rng.poisson(20 * c) / 20.0 # photon-counting noise of MA-XRF
return xrf.astype(np.float32), rgb.astype(np.float32)
train = [painting() for _ in range(6)]
xrf_t, rgb_t = painting() # a separate test painting
rgb_t[:, 15:26, 27:39] += np.array([0.06, -0.03, -0.04])[:, None, None] # retouching, chemistry unchanged
patch = np.zeros((S, S), bool); patch[15:26, 27:39] = True
def net(k):
p = k // 2
return torch.nn.Sequential(
torch.nn.Conv2d(4, 24, k, padding=p), torch.nn.ReLU(),
torch.nn.Conv2d(24, 24, k, padding=p), torch.nn.ReLU(),
torch.nn.Conv2d(24, 3, 1), torch.nn.Sigmoid())
X = torch.tensor(np.stack([a for a, _ in train])); Y = torch.tensor(np.stack([b for _, b in train]))
for name, k in [("pixel-wise (1x1)", 1), ("CNN (5x5)", 5)]:
model = net(k)
opt = torch.optim.Adam(model.parameters(), lr=3e-3)
for step in range(200):
opt.zero_grad()
loss = torch.mean((model(X) - Y) ** 2) # estimates E[VIS | XRF neighbourhood]
loss.backward(); opt.step()
with torch.no_grad():
pred = model(torch.tensor(xrf_t)[None])[0].numpy()
res = np.linalg.norm(rgb_t - pred, axis=0)
print(f"{name:16s}: RMSE outside the retouching {np.sqrt(np.mean(res[~patch] ** 2)):.4f}, "
f"residual AUC for the retouching {roc_auc_score(patch.ravel(), res.ravel()):.3f}")Key takeaways — chapter 3
- Densities of a few channels are easy to estimate: the kernel estimator converges at rate n^{-4/(d+4)}.
- Surprisal flags rare pixels; conditional surprisal flags values that the other channels do not explain.
- A CNN trained with a squared loss estimates the conditional expectation of colour given the chemistry around a pixel.
- Its residuals locate retouching; validate on separate image blocks.
4. Microstructure: craquelure and canvas
Some evidence lies in the microstructure of a painting: its cracks, and the weave of its canvas seen in radiographs.
4.1 Craquelure
Definition 4.1 — Crack types Drying cracks form while the paint dries and shrinks; they are often wide and irregular. Age cracks form later, through paint and ground, under the mechanical stresses of the support. On panels they tend to follow the wood grain. Bucklow (1997) described crack patterns by direction, island size and shape, smoothness and junction types, and related them to support and school.
Cracks are thin dark curves. At scale \sigma, the Hessian of L_\sigma has a large positive eigenvalue across a dark line and a small one along it.
Proposition 4.2 — Scale selection for lines Let a dark line have the Gaussian profile -e^{-x^2/(2w^2)}. The \gamma-normalised second derivative of L_\sigma at its centre is \sigma^{2\gamma}\, w\, (w^2 + \sigma^2)^{-3/2}. It is maximal at \sigma^2 = \frac{2\gamma}{3 - 2\gamma}\, w^2, \quad \text{so } \sigma = w \text{ for } \gamma = 3/4 .
Proof.
- Smoothing the profile gives L_\sigma(x) = -\frac{w}{\sqrt{w^2 + \sigma^2}}\, e^{-x^2 / (2(w^2 + \sigma^2))} , whose second derivative at x = 0 is w (w^2+\sigma^2)^{-3/2}.
- Set the derivative of 2\gamma \log\sigma - \tfrac32 \log(w^2 + \sigma^2) to zero: 2\gamma (w^2 + \sigma^2) = 3\sigma^2. ∎
So with \gamma = 3/4 (Lindeberg, 1998), the selected scale equals the crack width. A multi-scale line measure, as in the vesselness filter of Frangi et al. (1998), keeps pixels where the large eigenvalue dominates the small one.
Definition 4.3 — Orientation statistics of cracks Crack directions \theta_j \in [0, \pi) are axial: \theta and \theta + \pi are the same. Their coherence is the mean resultant length of the doubled angles, \bar R = \Big|\frac{1}{m} \sum_{j=1}^{m} e^{2 i \theta_j}\Big| \in [0, 1] .
Coherence near 1 means one dominant direction, as on many panels. With crack density, island areas and junction degrees, it feeds a classifier. The syllabus takes its examples from Van Eyck; Cornelis et al. (2013) detect cracks on the Ghent Altarpiece.
import numpy as np
from scipy import ndimage as ndi
rng = np.random.default_rng(0)
n, w = 256, 1.5 # image size, crack profile width (px)
def cracks(stretch):
"""Voronoi crack network; stretch > 1 elongates the islands along the vertical axis."""
seeds = rng.uniform(0, n, (60, 2))
yy, xx = np.mgrid[0:n, 0:n]
d = ((yy[..., None] - seeds[:, 0]) / stretch) ** 2 + (xx[..., None] - seeds[:, 1]) ** 2
lab = d.argmin(-1)
edge = (np.diff(lab, axis=0, append=lab[-1:]) != 0) | (np.diff(lab, axis=1, append=lab[:, -1:]) != 0)
img = 0.8 + 0.03 * rng.standard_normal((n, n)) # paint surface
img -= 0.5 * np.minimum(1, ndi.gaussian_filter(edge * 1.0, w) * np.sqrt(2 * np.pi) * w)
return img, edge
def ridge(img, sigmas, gamma=0.75):
"""Scale-normalised Hessian line measure for dark lines; best scale and local orientation."""
best, theta = np.zeros_like(img), np.zeros_like(img)
for s in sigmas:
Hyy = ndi.gaussian_filter(img, s, order=(2, 0)); Hxx = ndi.gaussian_filter(img, s, order=(0, 2))
Hxy = ndi.gaussian_filter(img, s, order=(1, 1))
root = np.sqrt(((Hxx - Hyy) / 2) ** 2 + Hxy ** 2)
lmax, lmin = (Hxx + Hyy) / 2 + root, (Hxx + Hyy) / 2 - root
r = s ** (2 * gamma) * np.maximum(lmax, 0) * np.exp(-(lmin / (lmax + 1e-9)) ** 2 / 0.5)
th = 0.5 * np.arctan2(2 * Hxy, Hxx - Hyy) + np.pi / 2 # direction along the line
upd = r > best
best[upd], theta[upd] = r[upd], th[upd]
return best, theta
for name, stretch in [("isotropic network (canvas-like)", 1.0), ("elongated islands (panel-like)", 4.0)]:
img, edge = cracks(stretch)
R, th = ridge(img, sigmas=[1.0, 1.5, 2.0, 3.0])
mask = R > 0.5 * np.percentile(R, 99)
truth = ndi.binary_dilation(edge, iterations=2)
precision, recall = (mask & truth).sum() / mask.sum(), (mask & edge).sum() / edge.sum()
Rbar = np.abs(np.mean(np.exp(2j * th[mask]))) # Definition 4.3
print(f"{name:32s}: precision {precision:.2f}, recall {recall:.2f}, "
f"crack density {mask.mean():.3f}, orientation coherence {Rbar:.2f}")
# Scale selection on a 1-D Gaussian line of width w: argmax of sigma^(2 gamma) L_xx(0)
sig = np.linspace(0.3, 6, 2000)
for gamma in (0.75, 1.0):
resp = sig ** (2 * gamma) * w * (w**2 + sig**2) ** -1.5
print(f"gamma = {gamma}: selected sigma / w = {sig[resp.argmax()] / w:.3f}")4.2 Canvas analysis from radiographs
In a radiograph, the lead-white ground that fills the gaps between threads draws the weave (Proposition 1.6). The threads running along a roll of canvas are the warp; the weft threads cross them.
Definition 4.4 — Thread density On a tile with pixel pitch \Delta (in cm), let the windowed Fourier spectrum peak at f cycles per pixel in the direction across the warp. The local warp density is \rho = f / \Delta \quad \text{threads per cm}.
Scanning tiles along the painting gives a warp-density profile; zero-padding and a parabolic fit locate the peak to a fraction of a bin.
Proposition 4.5 — Warp matching (weaving model) Assume that each warp thread runs the whole length of the roll, and that the spacing of neighbouring warp threads varies across the width but not along the length. Then two pieces cut from the same roll, at any positions along its length, have equal warp-spacing profiles over their common warp threads, up to a shift and possibly a flip. Weft threads, inserted one at a time, carry no such shared profile.
Matching therefore maximises the normalised correlation of two warp-density profiles over shifts and flips. The syllabus applies this to works by Poussin, Van Gogh, Rembrandt and Frans Hals; the method is described by Johnson, Johnson and Erdmann (2013).
Example 4.6 — How significant is a match? Under independence, Fisher’s transform z = \operatorname{atanh}(r)\sqrt{n - 3} is about standard normal. A correlation r = 0.6 over n = 50 independent samples gives z = 0.693 \times \sqrt{47} \approx 4.75 . But smooth profiles are not independent samples, and searching over shifts inflates the maximum: a null from unrelated canvases is safer.
import numpy as np
from scipy import ndimage as ndi
rng = np.random.default_rng(0)
px = 0.01 # pixel size in cm (0.1 mm)
def roll(width_cm=60, density=14.0):
"""Warp thread positions (cm) across the width of a roll: irregular, correlated spacings."""
k = int(width_cm * density)
eps = 0.08 * ndi.gaussian_filter1d(rng.standard_normal(k), 8) * np.sqrt(8) + 0.02 * rng.standard_normal(k)
return np.cumsum((1 + eps) / density)
def radiograph(warp, x0_cm, w_px=1536, h_px=256, weft_density=12.0):
"""X-ray image of a piece cut at x0 across the roll: the ground fills the interstices."""
x = x0_cm + px * np.arange(w_px)
u = np.interp(x, warp, np.arange(len(warp))) # warp thread phase
v = np.cumsum((1 + 0.05 * rng.standard_normal(h_px)) * weft_density * px) # weft phase
img = 0.2 * np.cos(2 * np.pi * u)[None, :] + 0.2 * np.cos(2 * np.pi * v)[:, None]
return img + 0.15 * rng.standard_normal((h_px, w_px))
def warp_profile(img, T=64, step=32, pad=512):
"""Warp thread density (threads/cm) along x, from the peak of windowed tile spectra."""
win = np.outer(np.hanning(T), np.hanning(T))
f = np.fft.rfftfreq(pad) / px # threads per cm
band = (f > 8) & (f < 20)
prof = []
for x in range(0, img.shape[1] - T + 1, step):
spec = [np.abs(np.fft.rfft2(img[y:y + T, x:x + T] * win, s=(pad, pad)))[0]
for y in range(0, img.shape[0] - T + 1, T)]
s = np.mean(spec, 0)
i = np.flatnonzero(band)[s[band].argmax()]
d = 0.5 * (s[i - 1] - s[i + 1]) / (s[i - 1] - 2 * s[i] + s[i + 1]) # parabolic refinement
prof.append((i + d) * (f[1] - f[0]))
return np.array(prof)
def best_match(a, b, min_overlap=24):
"""Largest normalised correlation of two density profiles over shifts (in tile steps)."""
out = []
for lag in range(-len(b) + min_overlap, len(a) - min_overlap + 1):
p, q = a[max(lag, 0):], b[max(-lag, 0):]
m = min(len(p), len(q))
out.append((np.corrcoef(p[:m], q[:m])[0, 1], lag))
return max(out)
R1 = roll()
A = warp_profile(radiograph(R1, x0_cm=10.0))
B = warp_profile(radiograph(R1, x0_cm=14.0)) # same roll, overlapping 11 cm of warp threads
print(f"mean warp density of A: {A.mean():.2f} threads/cm (the roll was woven at 14)")
same, lag = best_match(A, B)
null = np.array([best_match(A, warp_profile(radiograph(roll(), x0_cm=10.0)))[0] for _ in range(20)])
print(f"same roll: correlation {same:.2f} at a shift of {lag * 32 * px:.2f} cm (true: 4 cm)")
print(f"20 unrelated rolls: mean {null.mean():.2f}, max {null.max():.2f}")
print(f"z-score of the match against the unrelated rolls: {(same - null.mean()) / null.std():.1f}")Unrelated canvases reach 0.8 by chance: a match needs a calibrated null.
Key takeaways — chapter 4
- Cracks are dark ridges; Hessian filters with \gamma = 3/4 select their width.
- Crack networks are described by density, islands, junctions and orientation coherence.
- Tile spectra of radiographs give local thread densities.
- Shared warp profiles reunite pieces of one roll, given a calibrated null.
5. Text and image embeddings with pretrained networks
Pretrained networks map images and texts to vectors whose geometry reflects meaning, turning search and comparison into nearest-neighbour problems.
5.1 CLIP: a joint embedding of images and texts
Definition 5.1 — Contrastive image–text embedding An image encoder f and a text encoder g output unit vectors. For a batch of N pairs (x_i, t_i), let s_{ij} = \langle f(x_i), g(t_j) \rangle / \tau. The symmetric InfoNCE loss is \mathcal{L} = -\frac{1}{2N} \sum_{i=1}^{N} \Big[\log \frac{e^{s_{ii}}}{\sum_j e^{s_{ij}}} + \log \frac{e^{s_{ii}}}{\sum_j e^{s_{ji}}}\Big].
CLIP (Radford et al., 2021) trained such encoders on 400 million image–text pairs from the internet. A label set becomes a zero-shot classifier: embed prompts such as “a painting of a dog”, and pick the closest.
Proposition 5.2 — Duplicate captions set a floor Suppose that m captions of the batch, that of x_i included, cannot be told apart given the image x_i. Then the image-to-text term of pair i is at least \log m.
Proof. The m logits s_{ij} of those captions are equal, so the softmax gives the correct one a probability of at most 1/m. ∎
5.2 Search engine and semantic directions
A search engine embeds the collection once, then returns the images of largest cosine similarity to a text or image query. At millions of images, an approximate nearest-neighbour index such as FAISS (Johnson et al., 2021) replaces the exhaustive scan.
Definition 5.3 — Semantic direction For two prompts t^+ and t^- that differ in one concept, the direction is v = g(t^+) - g(t^-), and an image is scored by \langle f(x), v\rangle.
Directions such as “night scene” minus “day scene” rank a collection along a concept without any labelled example.
5.3 High-resolution microstructural comparison
Most CLIP models see 224 \times 224 pixels; a painting has billions. Embed tiles cut at native resolution, with unit embeddings a_1, \dots, a_m for one work and b_1, \dots, b_n for the other. Compare the two sets by the symmetric mean best-match similarity \mathrm{sim}(A, B) = \frac{1}{2m} \sum_{i=1}^{m} \max_{j} \langle a_i, b_j \rangle + \frac{1}{2n} \sum_{j=1}^{n} \max_{i} \langle a_i, b_j \rangle . Brushwork, craquelure or paper texture then become comparable. Micrographs are rare in CLIP’s training data, so trust relative, not absolute, similarities.
The code trains a miniature CLIP from scratch on the scikit-learn digits, with bag-of-words captions such as “a sketch of a seven on a light ground”.
import numpy as np
import torch
import torch.nn.functional as F
from sklearn.datasets import load_digits
from sklearn.metrics import roc_auc_score
torch.set_num_threads(1)
torch.manual_seed(0)
rng = np.random.default_rng(0)
digits = load_digits()
X = digits.data / 16.0
y = digits.target
inv = rng.random(len(X)) < 0.5 # half the images: light ground, dark ink
X[inv] = 1.0 - X[inv]
names = "zero one two three four five six seven eight nine".split()
templates = ["a drawing of the digit {}", "the number {} written by hand", "a sketch of a {}"]
ground = {False: "on a dark ground", True: "on a light ground"}
def caption(i):
return templates[rng.integers(3)].format(names[y[i]]) + " " + ground[bool(inv[i])]
vocab = {w: k for k, w in enumerate(sorted({w for t in templates for w in t.split()}
| set(names) | {"on", "a", "dark", "light", "ground"}))}
def tokens(texts):
"""Bag of words, as a normalised count matrix (a stand-in for a text transformer)."""
M = torch.zeros(len(texts), len(vocab))
for r, t in enumerate(texts):
for w in t.split():
M[r, vocab[w]] += 1
return M / M.sum(1, keepdim=True)
img_enc = torch.nn.Sequential(torch.nn.Linear(64, 128), torch.nn.ReLU(), torch.nn.Linear(128, 32))
txt_enc = torch.nn.Sequential(torch.nn.Linear(len(vocab), 128), torch.nn.ReLU(), torch.nn.Linear(128, 32))
log_scale = torch.nn.Parameter(torch.tensor(np.log(1 / 0.07), dtype=torch.float32))
params = list(img_enc.parameters()) + list(txt_enc.parameters()) + [log_scale]
opt = torch.optim.Adam(params, lr=2e-3)
train, test = np.arange(1300), np.arange(1300, len(X))
Xt = torch.tensor(X, dtype=torch.float32)
for step in range(400):
b = rng.choice(train, 256, replace=False)
zi = F.normalize(img_enc(Xt[b]), dim=1)
zt = F.normalize(txt_enc(tokens([caption(i) for i in b])), dim=1)
logits = log_scale.exp().clamp(max=100) * zi @ zt.T # s_ij of Definition 5.1
target = torch.arange(len(b))
loss = (F.cross_entropy(logits, target) + F.cross_entropy(logits.T, target)) / 2
opt.zero_grad(); loss.backward(); opt.step()
with torch.no_grad():
zi = F.normalize(img_enc(Xt[test]), dim=1)
enc = lambda texts: F.normalize(txt_enc(tokens(texts)), dim=1)
# Zero-shot classification with a prompt ensemble over the two grounds
zc = F.normalize(sum(enc([f"a drawing of the digit {d} {g}" for d in names]) for g in ground.values()), dim=1)
acc = ((zi @ zc.T).argmax(1).numpy() == y[test]).mean()
# Text-to-image search: the 10 nearest test images to one query
q = enc(["a sketch of a seven on a light ground"])
top = (zi @ q.T).squeeze(1).argsort(descending=True)[:10].numpy()
hits = np.mean((y[test][top] == 7) & inv[test][top])
# Semantic direction (Definition 5.3): light ground minus dark ground
d = enc(["a drawing of the digit five on a light ground"]) - enc(["a drawing of the digit five on a dark ground"])
score = (zi @ d.T).squeeze(1).numpy()
print(f"final loss {loss.item():.3f}, temperature {1 / log_scale.exp().item():.3f}")
print(f"zero-shot accuracy on {len(test)} held-out images: {acc:.3f}")
print(f"query 'a sketch of a seven on a light ground': precision at 10 = {hits:.1f}")
print(f"semantic direction 'light ground': AUC {roc_auc_score(inv[test], score):.3f}")5.4 DINOv2: self-supervised patch embeddings
DINOv2 (Oquab et al., 2024) trains vision transformers without labels on 142 million curated images. A student network matches a teacher, its moving average, across crops (DINO loss) and predicts masked patches (iBOT loss). The models cut images into patches of 14 \times 14 pixels and return one embedding per patch.
Definition 5.4 — Mutual nearest neighbours Let z_p and z'_q be the unit patch embeddings of two images. The pair (p, q) is a correspondence if each is the other’s most similar patch: q = \arg\max_{q'} \langle z_p, z'_{q'} \rangle \quad \text{and} \quad p = \arg\max_{p'} \langle z_{p'}, z'_q \rangle .
Because the embeddings encode parts rather than pixels, correspondences link the same figure in a painting, a copy and a print after it, despite changes of medium.
5.5 Visualisation by PCA
Fit a PCA to the patch embeddings of one or several images, and display the first three components as red, green and blue. Parts with similar meaning receive similar colours across images, and the first component often separates the subject from the background.
The code uses a contrast-invariant stand-in for the patch embeddings, on a scikit-learn sample image and a rotated, tone-inverted copy of it; DINOv2 patch tokens plug into the same pipeline.
import numpy as np
from scipy import ndimage as ndi
from sklearn.datasets import load_sample_image
from sklearn.decomposition import PCA
P = 14 # patch size of the DINOv2 ViT-*/14 models
img1 = load_sample_image("china.jpg").mean(2)[:420, :630] / 255.0
# A "copy": rotated by 8 degrees, scaled by 0.92, with an inverted, non-linear tone curve
a, s = np.deg2rad(8), 0.92
M = np.array([[np.cos(a), -np.sin(a)], [np.sin(a), np.cos(a)]]) / s
off = np.array(img1.shape) / 2 - M @ (np.array(img1.shape) / 2)
img2 = 1.0 - ndi.affine_transform(img1, M, offset=off, order=1, mode="nearest") ** 0.6
def patch_features(img, bins=4):
"""Stand-in for ViT patch tokens: orientation histograms (modulo pi, so contrast-invariant)
pooled in each 14 x 14 patch at two scales, concatenated over the 3 x 3 neighbouring patches."""
h, w = img.shape[0] // P, img.shape[1] // P
chans = []
for sg in (1.0, 2.0):
gy, gx = ndi.gaussian_filter(img, sg, order=(1, 0)), ndi.gaussian_filter(img, sg, order=(0, 1))
mag, th = np.hypot(gx, gy), np.arctan2(gy, gx)
for k in range(bins):
ch = mag * np.cos(th - np.pi * k / bins) ** 2 # axial orientation bin
chans.append(ch[:h * P, :w * P].reshape(h, P, w, P).mean((1, 3)))
f = np.stack(chans, -1) # (h, w, 2 * bins)
f = np.concatenate([np.roll(f, (dy, dx), (0, 1)) for dy in (-1, 0, 1) for dx in (-1, 0, 1)], -1)
f = np.sqrt(f.reshape(h * w, -1))
f -= f.mean(0)
return f / np.linalg.norm(f, axis=1, keepdims=True), (h, w)
f1, (h, w) = patch_features(img1)
f2, _ = patch_features(img2)
S = f1 @ f2.T # cosine similarities of all patch pairs
nn12, nn21 = S.argmax(1), S.argmax(0)
mutual = np.flatnonzero(nn21[nn12] == np.arange(len(f1))) # Definition 5.4
centre = lambda k: np.stack([k // w, k % w], 1) * P + P / 2
truth = (centre(np.arange(len(f1))) - off) @ np.linalg.inv(M).T # where each patch of img1 lands
seen = np.all((truth >= 0) & (truth < np.array(img2.shape)), axis=1)
ok = np.linalg.norm(centre(nn12) - truth, axis=1) < P # correct within one patch
print(f"plain nearest neighbours: {ok[seen].mean():.0%} correct ({seen.sum()} visible patches)")
print(f"mutual nearest neighbours: {len(mutual)} pairs, {ok[mutual].mean():.0%} correct")
# PCA of the patch features: three components mapped to RGB give a false-colour map
pca = PCA(n_components=3).fit(f1)
rgb = pca.transform(f1).reshape(h, w, 3)
rgb = (rgb - rgb.min((0, 1))) / (rgb.max((0, 1)) - rgb.min((0, 1)))
print("explained variance ratios:", pca.explained_variance_ratio_.round(3), "| false-colour map:", rgb.shape)Key takeaways — chapter 5
- CLIP aligns images and texts by a symmetric contrastive loss; prompts give classifiers and queries.
- Differences of text embeddings are semantic directions.
- Gigapixel images are compared through their tile embeddings.
- DINOv2 patches give correspondences (mutual nearest neighbours) and PCA maps.
6. Image-to-image translation with CNNs
Many conservation questions take an image and ask for another: the sheet without its ink, the colours of a lost strip.
6.1 The U-Net
The U-Net (Ronneberger et al., 2015) has a contracting path of 3 \times 3 convolutions and 2 \times 2 max pooling, and an expanding path of up-convolutions. Skip connections concatenate each encoder level to the decoder level of the same resolution, so fine detail survives the bottleneck. The original network uses unpadded convolutions and maps a 572 \times 572 input to a 388 \times 388 output; large images are processed tile by tile.
Proposition 6.1 — Seamless tiling Let every output pixel of a network depend only on inputs within distance \rho = (r - 1)/2, where r is the receptive field of ch. 0.6. Let s be the total stride of the network, 2^L after L poolings.
Run the network on overlapping tiles whose offsets are multiples of s, and keep, from each tile, only the pixels at distance at least \rho from its border. Then the assembled result equals the output on the whole image.
Proof. A kept pixel’s receptive field lies inside its tile, where tile and image coincide; padding effects reach at most \rho pixels from the border. Offsets that are multiples of s keep the pooling grids of tile and image aligned. ∎
6.2 VGG16, Gram matrices and style transfer
VGG16 (Simonyan and Zisserman, 2015) has 13 convolutional layers with 3 \times 3 kernels; its features, trained on ImageNet, serve as a perceptual measure. Gatys et al. (2016) separated content from style with them.
Definition 6.2 — Content and style losses Let F^l(x) \in \mathbb{R}^{N_l \times M_l} hold the feature maps of layer l at its M_l positions, and let G^l = F^l F^{l\top} be its Gram matrix.
For a content image c and a style image s, the pixels of x minimise \alpha \mathcal{L}_c + \beta \mathcal{L}_s, with \mathcal{L}_c = \tfrac12 \big\|F^{l_c}(x) - F^{l_c}(c)\big\|^2, \qquad \mathcal{L}_s = \sum_l \frac{w_l}{4 N_l^2 M_l^2} \big\|G^l(x) - G^l(s)\big\|_F^2 . Gatys et al. take the content at layer conv4_2 and the style at conv1_1 to conv5_1.
Proposition 6.3 — The style loss is a maximum mean discrepancy Let f_m and p_m be the columns of F, P \in \mathbb{R}^{N \times M}, and k(a, b) = (a^\top b)^2. Then \big\|F F^\top - P P^\top\big\|_F^2 = \sum_{m, m'} \big[k(f_m, f_{m'}) + k(p_m, p_{m'}) - 2\, k(f_m, p_{m'})\big], which is M^2 times the squared MMD between the two sets of columns. The Gram matrix ignores the order of positions.
Proof. F F^\top = \sum_m f_m f_m^\top, and \langle a a^\top, b b^\top \rangle_F = (a^\top b)^2; expand the squared norm. Permuting columns does not change \sum_m f_m f_m^\top. ∎
Style is thus matched as a distribution of local features, wherever they occur (Li et al., 2017). The code checks the identity, then synthesises a texture with random convolutional features, which already capture much of a texture (Ustyuzhaninov et al., 2017).
import numpy as np
import torch
from sklearn.datasets import load_sample_image
torch.set_num_threads(1)
torch.manual_seed(0)
# (1) Squared Gram distance = M^2 MMD^2 with the kernel k(x, y) = (x . y)^2 (Proposition 6.3)
N, M = 16, 200 # channels, positions
Fx, Fy = torch.randn(N, M, dtype=torch.float64), 1.3 * torch.randn(N, M, dtype=torch.float64)
G = lambda F: F @ F.T
k = lambda A, B: (A.T @ B) ** 2 # kernel between the columns (positions)
lhs = ((G(Fx) - G(Fy)) ** 2).sum()
rhs = k(Fx, Fx).sum() + k(Fy, Fy).sum() - 2 * k(Fx, Fy).sum()
print(f"Gram distance {lhs.item():.6e} = MMD form {rhs.item():.6e}")
print("Gram matrix invariant to shuffling the positions:", torch.allclose(G(Fx), G(Fx[:, torch.randperm(M)])))
# (2) Texture synthesis by Gram matching, with random convolutional features
ex = torch.tensor(load_sample_image("flower.jpg")[100:228:2, 200:328:2] / 255.0,
dtype=torch.float32).permute(2, 0, 1)[None] # 64 x 64 colour exemplar
layers = [torch.nn.Conv2d(3, 32, 5, padding=2), torch.nn.Conv2d(32, 32, 5, padding=2),
torch.nn.Conv2d(32, 32, 5, padding=2)]
for l in layers:
l.requires_grad_(False)
def grams(x):
out = []
for i, l in enumerate(layers):
x = torch.relu(l(x if i == 0 else torch.nn.functional.avg_pool2d(x, 2)))
f = x.flatten(2)[0]
out.append(f @ f.T / f.shape[1])
return out
target = grams(ex)
img = torch.rand_like(ex).requires_grad_(True)
opt = torch.optim.Adam([img], lr=0.03)
style = lambda x: sum(((g - t) ** 2).sum() for g, t in zip(grams(x), target))
print(f"style loss at start: {style(img).item():.4f}")
for step in range(250):
opt.zero_grad()
loss = style(img)
loss.backward(); opt.step()
img.data.clamp_(0, 1)
centred = lambda t: (t - t.mean((2, 3), keepdim=True)).detach().flatten().numpy()
corr = np.corrcoef(centred(img), centred(ex))[0, 1]
print(f"style loss after 250 steps: {style(img).item():.4f}; "
f"pixel correlation with the exemplar (channel means removed): {corr:.2f}")
print(f"channel means: exemplar {ex.mean((0, 2, 3)).numpy().round(2)}, synthesis {img.detach().mean((0, 2, 3)).numpy().round(2)}")6.3 Case study: the Magic Eraser
In transmitted light, a sheet shows its watermark, chain lines and laid lines, which date and locate the paper; ink drawn on it hides them. By Proposition 1.6, in the log domain \log I = \log T_{\text{paper}} - d_{\text{ink}}, \qquad d_{\text{ink}} \ge 0 . A U-Net trained on synthetic pairs, clean sheets with simulated strokes, predicts the non-negative d_{\text{ink}}; adding it back erases the ink. In one channel, a bright watermark under ink looks like thinner ink; the shape of strokes, or a coloured ink, resolves the ambiguity. The revealed watermark is compared with repertories such as Briquet’s (1907).
import numpy as np
import torch
import torch.nn as nn
from scipy import ndimage as ndi
torch.set_num_threads(1)
torch.manual_seed(0)
rng = np.random.default_rng(0)
S = 40
tint = np.log([0.95, 0.90, 0.80])[:, None, None] # colour of the paper in transmitted light
a_ink = np.array([0.6, 1.0, 1.6])[:, None, None] # optical density of a brown ink per channel
def sheet():
"""Log transmitted light (R, G, B) of laid paper with a watermark, covered by pen strokes."""
yy, xx = np.mgrid[0:S, 0:S]
paper = 0.6 + 0.03 * np.sin(2 * np.pi * yy / 2.2) # laid lines
paper += 0.08 * (np.abs((xx + rng.integers(50)) % 50 - 25) < 1) # a chain line
cy, cx, r = rng.uniform(12, 28), rng.uniform(12, 28), rng.uniform(5, 10)
ring = np.abs(np.hypot(yy - cy, xx - cx) - r) < 0.8 # watermark: thinner paper
paper += 0.12 * ring + 0.02 * ndi.gaussian_filter(rng.standard_normal((S, S)), 3)
ink = np.zeros((S, S))
for _ in range(rng.integers(2, 5)): # quadratic Bezier strokes
t = np.linspace(0, 1, 200)[:, None]
p0, p1, p2 = rng.uniform(0, S, (3, 2))
cv = np.clip(((1 - t) ** 2 * p0 + 2 * t * (1 - t) * p1 + t ** 2 * p2).astype(int), 0, S - 1)
ink[cv[:, 0], cv[:, 1]] = 1
ink = ndi.gaussian_filter(ndi.binary_dilation(ink, iterations=rng.integers(1, 3)) * 1.0, 0.7)
clean = np.log(paper)[None] + tint
seen = clean - a_ink * ink + 0.01 * rng.standard_normal((3, S, S)) # Beer-Lambert: additive in log
return seen, clean, ring, ink > 0.3
def unet(cin, c=8):
"""A two-level U-Net whose output adds back a non-negative ink density."""
block = lambda i, o: nn.Sequential(nn.Conv2d(i, o, 3, padding=1), nn.ReLU(), nn.Conv2d(o, o, 3, padding=1), nn.ReLU())
class UNet(nn.Module):
def __init__(self):
super().__init__()
self.e1, self.e2, self.mid = block(cin, c), block(c, 2 * c), block(2 * c, 4 * c)
self.u2, self.d2 = nn.ConvTranspose2d(4 * c, 2 * c, 2, stride=2), block(4 * c, 2 * c)
self.u1, self.d1 = nn.ConvTranspose2d(2 * c, c, 2, stride=2), block(2 * c, c)
self.out = nn.Conv2d(c, cin, 1)
nn.init.zeros_(self.out.weight); nn.init.constant_(self.out.bias, -1.0)
def forward(self, x):
e1 = self.e1(x)
e2 = self.e2(nn.functional.max_pool2d(e1, 2))
m = self.mid(nn.functional.max_pool2d(e2, 2))
d2 = self.d2(torch.cat([self.u2(m), e2], 1)) # skip connection
d1 = self.d1(torch.cat([self.u1(d2), e1], 1)) # skip connection
return x + nn.functional.softplus(self.out(d1)) # ink only darkens: add back >= 0
return UNet()
train = [sheet() for _ in range(2400)]
test = [sheet() for _ in range(50)]
def visibility(img, b):
"""Watermark contrast-to-clutter ratio on the inked pixels (log units)."""
on, off = img[b[2] & b[3]], img[~b[2] & b[3]]
return (on.mean() - off.mean()) / off.std()
model = unet(3)
opt = torch.optim.Adam(model.parameters(), lr=3e-3)
for step in range(150):
batch = train[16 * step: 16 * step + 16]
x = torch.tensor(np.stack([b[0] for b in batch]), dtype=torch.float32)
y = torch.tensor(np.stack([b[1] for b in batch]), dtype=torch.float32)
loss = nn.functional.mse_loss(model(x), y)
opt.zero_grad(); loss.backward(); opt.step()
with torch.no_grad():
out = model(torch.tensor(np.stack([b[0] for b in test]), dtype=torch.float32)).numpy()
keep = [i for i, b in enumerate(test) if (b[2] & b[3]).sum() > 3] # watermark crosses the ink
for name, imgs in [("inked input", [b[0] for b in test]), ("after erasing", out), ("clean paper", [b[1] for b in test])]:
g = [imgs[i].mean(0) for i in range(len(test))] # grey level, log units
err = np.mean([np.sqrt(np.mean((g[i] - test[i][1].mean(0))[test[i][3]] ** 2)) for i in keep])
vis = np.mean([visibility(g[i], test[i]) for i in keep])
print(f"{name:13s}: RMS error under the ink {err:.3f}, watermark visibility {vis:5.2f}")After 150 steps on a CPU, the error under the ink drops tenfold, but the watermark is still far less visible than on the clean sheet: closing that gap is Exercise 19.
6.4 Case study: the Night Watch
In 1715 Rembrandt’s Night Watch was cut down on all sides, most of all on the left, to fit a wall of Amsterdam’s town hall. A smaller seventeenth-century copy attributed to Gerrit Lundens, in the National Gallery, London, shows the full composition. The Rijksmuseum showed reconstructed strips with the painting in 2021. A reconstruction of this kind has four steps.
- Register the copy to the painting on the region both show (ch. 2); the copy’s proportions are not exact, so the warp must be local.
- On that overlap \Omega_o, learn a translation from the copy’s handling and colours to Rembrandt’s (ch. 6.1–6.2).
- Apply it on the missing strips \Omega_m.
- Validate on strips of \Omega_o held out from training, with the errors reported.
The result is a hypothesis about the lost parts, not a restoration: the network extrapolates from \Omega_o to \Omega_m.
Key takeaways — chapter 6
- U-Net skip connections keep fine detail; overlap tiles by the receptive-field radius.
- The Gram style loss is an MMD between distributions of local features.
- Ink removal works in the log domain, with a non-negative ink density.
- A reconstruction learned on an overlap is a hypothesis, validated on held-out strips.
7. Complements: optics of paint, information and uncertainty
This self-study chapter deepens four points of the course: why pigments mixed in one layer do not mix linearly, how to compare modalities without learning, why contrastive learning needs large batches, and how to attach honest error bars to a reconstruction.
7.1 Kubelka–Munk: the optics of a paint layer
Proposition 1.4 followed light through the paint once. In a turbid layer, light is absorbed and scattered many times. The two-flux model of Kubelka and Munk (1931) follows a downward flux i(z) and an upward flux j(z) at depth z, with absorption coefficient K and scattering coefficient S: \frac{di}{dz} = -(K + S)\, i + S\, j, \qquad \frac{dj}{dz} = (K + S)\, j - S\, i .
Proposition 7.1 — Reflectance of an opaque layer For a layer thick enough to hide its support, the reflectance R_\infty = j/i at the surface satisfies R_\infty = 1 + \frac{K}{S} - \sqrt{\Big(\frac{K}{S}\Big)^2 + 2\,\frac{K}{S}}, \qquad \frac{K}{S} = \frac{(1 - R_\infty)^2}{2 R_\infty} .
Proof.
- Let \rho = j/i. Differentiating and using the two equations gives \frac{d\rho}{dz} = -S\rho^2 + 2(K + S)\rho - S .
- In an infinitely thick layer the ratio does not depend on depth, so S\rho^2 - 2(K + S)\rho + S = 0; the root in [0, 1] is R_\infty.
- The quadratic says R_\infty + 1/R_\infty = 2(1 + K/S), which rearranges into the second formula. ∎
Definition 7.2 — Mixing rule For pigments with absorption and scattering spectra K_i(\lambda), S_i(\lambda) per unit concentration, mixed at concentrations c_i in one layer, \Big(\frac{K}{S}\Big)_{\text{mix}} = \frac{\sum_i c_i K_i}{\sum_i c_i S_i} .
The reflectance of a mixture is therefore not a mixture of reflectances. But the rule is linear in c once q = (K/S)_{\text{mix}} is computed from the measured reflectance: \sum_i c_i\, \big(K_i(\lambda) - q(\lambda)\, S_i(\lambda)\big) = 0 \quad \text{for every } \lambda . With \sum_i c_i = 1, non-negative least squares recovers the concentrations. The code compares the two unmixings for a strong blue in a white.
import numpy as np
from scipy.optimize import nnls
lam = np.linspace(400, 700, 61)
# Absorption K and scattering S (per unit concentration) of two synthetic pigments
K = np.stack([0.02 + 0 * lam, # white: weak absorption
0.05 + 2.0 / (1 + np.exp(-(lam - 560) / 15))], 1) # blue: absorbs red light
S = np.stack([1.0 + 0 * lam, 0.3 + 0 * lam], 1) # white scatters strongly
def R_inf(k_over_s):
"""Reflectance of an opaque layer (Proposition 7.1)."""
return 1 + k_over_s - np.sqrt(k_over_s**2 + 2 * k_over_s)
c_true = np.array([0.8, 0.2]) # concentrations
r_mix = R_inf((K @ c_true) / (S @ c_true)) # mixing rule (Definition 7.2)
R_pure = R_inf(K / S) # spectra of the pure pigments
# (a) Linear unmixing of reflectances, as in Definition 1.5
w = 100.0
a_lin, _ = nnls(np.vstack([R_pure, w * np.ones(2)]), np.append(r_mix, w))
# (b) Unmixing in K/S space: sum_i c_i (K_i - q S_i) = 0 with q = (K/S) of the mixture
q = (1 - r_mix) ** 2 / (2 * r_mix)
A = K - q[:, None] * S
c_km, _ = nnls(np.vstack([A, w * np.ones(2)]), np.r_[np.zeros(len(lam)), w])
print("true concentrations :", c_true)
print("linear unmixing :", a_lin.round(3))
print("Kubelka-Munk unmixing :", c_km.round(3))
print(f"reflectance at 650 nm: white {R_pure[50, 0]:.3f}, blue {R_pure[50, 1]:.3f}, mixture {r_mix[50]:.3f}")A fifth of blue pulls the reflectance at 650 nm from 0.82 to 0.38, so linear unmixing reads about half blue. In the infrared, both K and S of most paints decrease, so a layer of given thickness hides less of what lies below: this is the physics behind ch. 1.2.
7.2 Mutual information as a similarity
Definition 7.3 — Mutual information For random variables U and V with joint law p_{UV}, I(U; V) = \mathrm{KL}\big(p_{UV} \,\big\|\, p_U \otimes p_V\big) = H(U) + H(V) - H(U, V) \ge 0 .
Proposition 7.4 — Invariance to recoding For discrete variables and any bijection \phi of the values of U, I(\phi(U); V) = I(U; V).
Proof. A bijection only relabels the rows of the joint probability table; the entropies H(U) and H(U, V) are sums over entries and do not depend on labels. ∎
So mutual information, estimated from the joint histogram of the intensities of two overlapping images, tolerates any one-to-one intensity relation, monotone or not. Registration by maximisation of mutual information over the transform parameters was introduced by Viola and Wells (1997) and Maes et al. (1997). It needs no training data, but its histograms are noisy on small tiles, where the learned filters of Definition 2.11 are more precise.
7.3 Contrastive learning and mutual information
Proposition 7.5 — The InfoNCE bound Let (X, Y) be a positive pair, and Y_2, \dots, Y_N independent negatives drawn from the marginal of Y. For every score function, the expected image-to-text InfoNCE loss \mathcal{L}_N of Definition 5.1 satisfies I(X; Y) \ge \log N - \mathcal{L}_N .
Proof. Sketch (van den Oord et al., 2018; Poole et al., 2019). The loss is the cross-entropy of guessing which of N candidates is the positive. The guess is no better than the Bayes posterior, whose expected log-probability is bounded through the density ratio p(y \mid x)/p(y); averaging that ratio over the negatives gives the bound. ∎
The bound cannot exceed \log N, so estimating large dependencies requires many negatives: CLIP used batches of 32,768 pairs. Duplicates in a batch (Proposition 5.2) raise the floor of the loss without saying anything about the encoders.
7.4 Honest error bars for reconstructions
A reconstruction such as that of ch. 6.4 is validated on held-out strips. Split conformal prediction turns those strips into a guaranteed interval.
Proposition 7.6 — Split conformal coverage Let e_1, \dots, e_n be the absolute errors of a fixed predictor on calibration points, and e_{n+1} its error on a new point, the n + 1 errors being exchangeable.
Let \hat q be the \lceil (n+1)(1-\alpha) \rceil-th smallest of e_1, \dots, e_n, or +\infty if that index exceeds n. Then \mathbb{P}\big(e_{n+1} \le \hat q\big) \ge 1 - \alpha .
Proof. By exchangeability, the rank of e_{n+1} among the n + 1 errors is uniform on \{1, \dots, n+1\} (ties broken at random). The event e_{n+1} \le \hat q contains the event that this rank is at most \lceil (n+1)(1-\alpha) \rceil, which has probability at least 1 - \alpha. ∎
The guarantee is marginal, and it rests on exchangeability. Neighbouring pixels are dependent, and a missing strip differs from the overlap; calibrate on whole held-out blocks, and report the interval as conditional on that assumption (Vovk et al., 2005; Lei et al., 2018).
Key takeaways — chapter 7
- In a turbid layer, K/S = (1 - R_\infty)^2 / (2R_\infty), and mixtures are linear in K/S, not in reflectance.
- Mutual information is invariant to any one-to-one recoding of intensities.
- InfoNCE bounds mutual information by \log N minus the loss: large batches are needed.
- Split conformal prediction gives coverage 1 - \alpha under exchangeability; spatial data need block-wise calibration.
8. Research directions and open problems
This self-study chapter surveys active questions, with entry points in the literature. Each item names a problem that the methods of the course do not yet solve.
8.1 Registration and matching
- Detector-free matchers, such as LoFTR (Sun et al., 2021) and RoMa (Edstedt et al., 2024), and fast learned matchers, such as LightGlue (Lindenberger et al., 2023), have changed matching between photographs.
- Open problem: matching between modalities that share few edges, such as element maps and photographs, at gigapixel scale and with sub-pixel accuracy, with reliable estimates of the local error.
8.2 From spectra and element maps to materials
- Reflectance spectra, element maps and radiographs see the same layers through different physics. Hyperspectral unmixing is reviewed by Bioucas-Dias et al. (2012); the physics of mixtures is that of ch. 7.1.
- Open problems: joint inversion of RIS and MA-XRF into pigment maps with uncertainty, and models of layering, where the order of layers matters as much as their content.
8.3 Microstructure
- Convolutional networks now detect cracks in paintings, as in the work of Sizyakin et al. (2020).
- Open problems: a typology of craquelure learned from data and related to supports and schools, as Bucklow (1997) did by hand; and standards of statistical evidence for canvas matches, since profiles are autocorrelated and searches over shifts are multiple comparisons (Example 4.6).
8.4 Foundation models and digitised collections
- Work by Shen, Efros and Aubry (2019) discovers repeated motifs in art collections with spatially consistent features. Elgammal et al. (2018) studied how networks trained on style labels organise art history. Mensink and van Gemert (2014) built a recognition benchmark from the Rijksmuseum collection.
- Open problems: the domain gap between web photographs and paintings, drawings or micrographs; fine-tuning on small expert-labelled sets; and search interfaces whose semantic directions are checked by art historians, not only by retrieval scores.
8.5 Generative reconstruction
- Inpainting has moved from partial convolutions (Liu et al., 2018) to diffusion models, such as RePaint (Lugmayr et al., 2022).
- Open problems: calibrated uncertainty for reconstructed regions (ch. 7.4), and conventions to disclose what is measured, what is inferred and what is invented in an image shown to the public.
8.6 Attribution and connoisseurship
- Tummers (2011) examines how connoisseurs authenticate paintings by Rembrandt and his contemporaries. Computational studies have proposed statistical signatures of artists: Lyu, Rockmore and Farid (2004) used wavelet statistics, and Johnson et al. (2008) compared several image-processing approaches to attributing paintings to Van Gogh from his brushwork.
- Open problems: small data sets, workshops where several hands painted one work, deliberate forgeries, and the communication of evidence as calibrated probabilities that a connoisseur can weigh against other evidence.
Key takeaways — chapter 8
- Learned matching works between photographs; matching across physical modalities remains open.
- Joint inversion of spectral, elemental and radiographic data into layered material maps is a frontier.
- Foundation models need adaptation, and expert checks, to serve art history.
- Reconstructions and attributions need calibrated uncertainty and transparent communication.
Exercises
★ The size of a multimodal stack. A panel of 80 \times 60 cm is imaged in VIS at 10 µm per pixel (3 channels, 16 bits), in IRR at 50 µm (1 channel, 16 bits), in RIS at 0.25 mm (300 bands, 16 bits) and in MA-XRF at 0.5 mm (spectra of 4096 channels of 32 bits). (a) Compute the size of each image. (b) How many 64 \times 64 tiles does the VIS image hold?
Solution: (a) VIS: 80\,000 \times 60\,000 \times 6 bytes = 28.8 GB. IRR: 16\,000 \times 12\,000 \times 2 = 0.38 GB. RIS: 3200 \times 2400 \times 600 = 4.6 GB. MA-XRF: 1600 \times 1200 \times 16\,384 = 31.5 GB. The stack holds about 65 GB.
(b) 4.8 \times 10^9 / 4096 \approx 1.17 million tiles: every per-tile computation must be batched (ch. 2.5).
★ Moseley’s law and a modern pigment. (a) Estimate the K\alpha energies of titanium (Z = 22) and zinc (Z = 30) with Proposition 1.7, and compare with the tabulated 4.51 and 8.64 keV. (b) An MA-XRF scan of a seventeenth-century painting shows a strong titanium signal confined to a small patch. What does it suggest, and what should be checked? (c) How would you tell an arsenic pigment from lead white?
Solution: (a) 10.2 \text{ eV} \times 21^2 \approx 4.50 keV and 10.2 \text{ eV} \times 29^2 \approx 8.58 keV: within 1 %.
(b) Titanium white is a pigment of the twentieth century, so a confined signal suggests a later retouching. Check its shape against the photograph, since traces of titanium also occur in some natural earths. (c) As K\alpha and Pb L\alpha coincide near 10.5 keV (Example 1.8); compare the As K\beta (11.73 keV) and Pb L\beta (12.61 keV) maps.
★ Phase shifting by hand. At one pixel the three fringe images of Proposition 1.9 give 0.9, 0.3 and 0.6. (a) Compute \varphi, A and B. (b) The reference phase there is 0, with p = 2 mm and \theta = 30^\circ: compute the height. (c) Which heights can be measured without phase unwrapping?
Solution: (a) By Proposition 1.9, \varphi = \operatorname{atan2}(\sqrt3 \times 0.3,\ 0.9) = \pi/6 . A is the mean, 0.6, and 3B\cos\varphi = 0.9 gives B \approx 0.346.
(b) The height is h = \frac{\varphi\, p}{2\pi\tan\theta} = \frac{(\pi/6) \times 2}{2\pi \times 0.577} \approx 0.289 \text{ mm}.
(c) Without unwrapping, |\Delta\varphi| < \pi, that is |h| < p / (2\tan\theta) \approx 1.73 mm.
★ A RANSAC budget. (a) A quarter of the matches between an X-ray and a photograph are correct. How many draws give a 99 % chance of an all-inlier sample for a homography? (b) And for an affine transform (s = 3)? (c) Why is a fixed inlier threshold risky on a large painting photographed with a wide-angle lens?
Solution: (a) \log 0.01 / \log(1 - 0.25^4) \approx 1176.6, so 1177 draws (Proposition 2.6). (b) \log 0.01 / \log(1 - 0.25^3) \approx 292.4, so 293 draws.
(c) Residual lens distortion makes the errors of correct matches grow towards the corners (Example 2.5). A threshold tuned at the centre rejects good matches at the edges, which motivates the σ-marginalised weights of Proposition 2.7.
★ When correlation fails. (a) Prove Proposition 2.9. (b) Let a = (1, 2, 3, 4) and b = (a - 2.5)^2 componentwise. Compute \mathrm{NCC}(a, b). (c) Which representation of a and b makes them correlate?
Solution: (a) Centring removes \beta; the factor \alpha appears in the numerator and |\alpha| in the denominator.
(b) The centred vectors are (-1.5, -0.5, 0.5, 1.5) and (1, -1, -1, 1), whose inner product is 0: \mathrm{NCC} = 0 although b is a function of a. (c) A representation unchanged by the map, such as the magnitude of a smoothed gradient on images, as in the code of ch. 2.5, or mutual information (ch. 7.2).
★ Thread density from a spectral peak. A 64 \times 64 tile of a radiograph at 0.1 mm per pixel is zero-padded to 512 points. (a) The spectral peak is at index 72: compute the thread density. (b) Compare the bin width with and without padding. (c) The spectrum at indices 71, 72, 73 is 0.8, 1.0, 0.9: refine the estimate.
Solution: (a) f = 72/512 cycles per pixel, so \rho = 0.1406 / 0.01 \approx 14.06 threads per cm (Definition 4.4). (b) 0.195 threads per cm with padding, 1.56 without.
(c) The parabola gives \delta = 0.5 \times (0.8 - 0.9)/(0.8 - 2 + 0.9) \approx 0.167, so \rho \approx 72.17/512/0.01 \approx 14.10 threads per cm.
★ Temperature and retrieval. A query has cosine similarities 0.31, 0.29, 0.25 and 0.10 with four images. (a) Compute the softmax probabilities for \tau = 0.01 and \tau = 0.1. (b) Does the temperature change the ranking returned by a search engine? (c) What does it change in training?
Solution: (a) For \tau = 0.01: 0.879, 0.119, 0.002, 0.000. For \tau = 0.1: 0.402, 0.329, 0.220, 0.049.
(b) No: the softmax is increasing in each similarity. (c) In the loss of Definition 5.1, a small \tau concentrates the gradient on the hardest negatives; CLIP learns \tau (the code starts from 0.07).
★ Tiling a gigapixel image. (a) Compute the receptive field of two 3 \times 3 convolutions, a 2 \times 2 pooling, then two 3 \times 3 convolutions (ch. 0.6). (b) A U-Net with two poolings and a receptive field of 93 pixels runs on tiles of 512 pixels. By Proposition 6.1, what core is kept per tile, and how many tiles cover a 40\,000 \times 30\,000 image? (c) What is the computational overhead?
Solution: (a) r = 1 + 2 + 2 + 1 + 2 \times 2 + 2 \times 2 = 14.
(b) The margin is \rho = 46, so the core is 512 - 92 = 420 pixels, and the number of tiles is \lceil 40\,000/420 \rceil \times \lceil 30\,000/420 \rceil = 96 \times 72 = 6912 . The step of 420 pixels is a multiple of the total stride s = 4, as Proposition 6.1 requires. With four poolings (s = 16), the step drops to 416 pixels and the count rises to 97 \times 73 = 7081 tiles.
(c) (512/420)^2 \approx 1.49: about 49 % more computation than one pass over the image.
★ Mutual nearest neighbours. The cosine similarities between three patches of a painting (rows) and three patches of a print after it (columns) are S = \begin{pmatrix} 0.9 & 0.2 & 0.4 \\ 0.8 & 0.3 & 0.1 \\ 0.1 & 0.6 & 0.5 \end{pmatrix}. (a) Find the correspondences of Definition 5.4. (b) Why is the second patch left unmatched, and why is that desirable?
Solution: (a) Row maxima: 1→1, 2→1, 3→2. Column maxima: 1→1, 2→3, 3→3. The mutual pairs are (1, 1) and (3, 2).
(b) Patch 2 prefers column 1, which prefers patch 1: two patches compete for one partner. Discarding such one-sided matches removes most errors, as the code of ch. 5.5 shows (61 % against 82 % correct).
★★ The homography of a plane. (a) Write the two linear equations in H given by one match, and explain why four matches in general position determine H. (b) Deduce from Proposition 2.3 that a camera rotating about its centre also gives a homography, whatever the scene. (c) Show that H is affine when K = K', R = I, the plane is n = (0, 0, 1)^\top and the translation is parallel to it.
Solution: (a) With x' = (u, v, 1) and rows h_1, h_2, h_3 of H: h_1^\top x - u\, h_3^\top x = 0 and h_2^\top x - v\, h_3^\top x = 0. H has 8 degrees of freedom (scale is free), and four points, no three collinear, give 8 independent equations.
(b) With t = 0, H = K' R K^{-1} does not depend on the plane: every point maps the same way.
(c) Here H = I + K t n^\top K^{-1} / d . With n = e_3 and t_3 = 0, the third entry of K t is zero, because the last row of K is e_3^\top. So H keeps the last row (0, 0, 1) of the identity: it is affine.
★★ The σ-marginalised weight. (a) Check Proposition 2.7 numerically for r = 1 and \sigma_{\max} = 2 by quadrature. (b) Compute the weight w(r) = \operatorname{erfc}\big(r / (\sqrt2\,\sigma_{\max})\big) at r equal to one, two and three times \sigma_{\max}, and compare with a hard threshold at \sigma_{\max}. (c) Show that p(r) integrates to 1.
Solution: (a) Both sides equal 0.3867.
(b) 0.317, 0.046 and 0.003: matches just beyond the scale still count a little, far ones not at all, and no single threshold has to be chosen.
(c) With u = r/(\sqrt2\,\sigma_{\max}) and \int_0^\infty \operatorname{erfc}(u)\,du = 1/\sqrt\pi, the integral is \frac{1}{\sigma_{\max}} \sqrt{\frac{\pi}{2}} \times \frac{\sqrt2\,\sigma_{\max}}{\sqrt\pi} = 1 .
★★ Dimension and surprisal. (a) Minimise c_1 h^4 + c_2/(n h^d) in h and give the resulting rate (Theorem 3.2). (b) By which factor must n grow to halve the mean squared error for d = 4, and for the d = 300 bands of a RIS cube? What follows for surprisal maps of spectra? (c) Prove Proposition 3.4.
Solution: (a) h^\star = \big(d\, c_2 / (4 c_1 n)\big)^{1/(d+4)}, and the error decreases as n^{-4/(d+4)}.
(b) By 2^{(d+4)/4}: a factor 4 for d = 4, and 2^{76} for d = 300. Spectra must first be reduced to a few components (PCA or unmixing, Definition 1.5).
(c) Given U = u, V is normal with mean m(u), linear in u, and variance s^2; take minus the logarithm of its density.
★★ Classifying crack networks. (Python) In the code of ch. 4.1: (a) compute three features per image, the crack density, the orientation coherence of Definition 4.3 and the logarithm of the mean island area (label the connected components of the complement of the mask with
scipy.ndimage.label). (b) Generate 30 networks with stretch 1 and 30 with stretch 2, and estimate the 5-fold cross-validated accuracy of a logistic regression on each feature alone and on all three. (c) Repeat with stretch 1.5, and say which feature carries the classification.Hints and expected result: (a)–(b) With stretch 2, coherence alone separates the classes (accuracy about 1.0, mean coherence about 0.12 against 0.52). Density reaches about 0.97 and island area about 0.7.
(c) With stretch 1.5, coherence still gives about 0.98, while density falls to about 0.78 and island area to near chance (about 0.57). Real crack types differ in more than anisotropy: width, sharpness, junctions, support and restorations vary from painting to painting. Validate on whole held-out paintings, as Exercise 18 explains.
★★ How significant is a canvas match? (Python) In the code of ch. 4.2: (a) apply Fisher’s transform of Example 4.6 to the same-roll correlation, over its n = 34 overlapping tiles at the best shift, and to the largest unrelated one, with the minimum overlap n = 24. (b) Estimate the lag-one autocorrelation \rho_1 of the profiles and the effective sample size. (c) Replace the 20 unrelated rolls by 200, and report an empirical p-value.
Hints and expected result: (a) z \approx 1.832 \times \sqrt{31} \approx 10.2 for r = 0.95, but also z \approx 5.2 for the chance correlation of 0.81: the naive test would call both significant.
(b) The profiles have \rho_1 \approx 0.8. For two independent AR(1)-like profiles with lag-one autocorrelations \rho_1 and \rho_1', Bartlett’s approximation gives n_{\text{eff}} \approx n\, \frac{1 - \rho_1 \rho_1'}{1 + \rho_1 \rho_1'} , about 8 of the 34 tiles here.
(c) With k exceedances among N unrelated rolls, the empirical p-value is (1 + k)/(1 + N); with none in 200, report p \le 1/201 \approx 0.005.
★★ The floor of a contrastive loss. (Python) In the code of ch. 5.3, the image cannot know which of the three templates its caption used. (a) With 20 classes (digit and ground) in batches of 256, estimate the floor of Proposition 5.2 and compare with the printed final loss. (b) Prove the analogue of Proposition 5.2 for the text-to-image term. (c) Modify the loss so that all captions of the same class count as positives, and observe the new final loss.
Hints and expected result: (a) Each image has on average 1 + 255/20 \approx 13.75 indistinguishable captions in the batch, its own included, so the floor is about \log 13.75 \approx 2.6; the code prints 2.601.
(b) Images of one class with the same caption are interchangeable for the text encoder, so the same argument applies by columns.
(c) The answer depends on the form of the loss, with p_{ij} the softmax probabilities of each row and P(i) the captions of its class. With -\log \sum_{j \in P(i)} p_{ij}, the probability mass on all positives, the floor is 0 and the loss approaches it (about 0.002 after 400 steps). With the average of -\log p_{ij} over P(i), the floor stays \log m (about 2.59). The retrieval metrics barely change in either case.
★★ What the Gram matrix sees. (a) Prove \langle a a^\top, b b^\top \rangle_F = (a^\top b)^2 and deduce Proposition 6.3. (b) For a single feature channel, show that the style loss only compares the mean of f_m^2. (c) Show that the Gram matrix of centred features is M times their covariance, and relate this to matching channel means and variances, as adaptive instance normalisation does (Huang and Belongie, 2017).
Solution: (a) For vectors a and b, \langle a a^\top, b b^\top \rangle_F = \operatorname{tr}(a a^\top b b^\top) = (a^\top b)^2 ; expand the squared norm of \sum_m f_m f_m^\top - \sum_m p_m p_m^\top with it.
(b) For N = 1, G = \sum_m f_m^2, a scalar: two images with the same energy have the same style, whatever their arrangement.
(c) The Gram matrix of centred features is \sum_m (f_m - \bar f)(f_m - \bar f)^\top = M\, \widehat{\mathrm{Cov}} . AdaIN matches only the diagonal (means and variances per channel), a cheaper and weaker version of Gram matching.
★★ Kubelka–Munk unmixing. (Python) (a) From Proposition 7.1, compute K/S for R_\infty = 0.82, 0.38 and 0.064, and check against the code of ch. 7.1. (b) Explain why linear unmixing overestimates the blue. (c) Add 1 % noise and a third pigment to the code, and compare the errors of the two unmixings over 100 noisy draws.
Hints and expected result: (a) 0.0198, 0.506 and 6.84; the blue at 650 nm has K/S = 2.045/0.3 \approx 6.8, and the mixture 0.425/0.86 \approx 0.49.
(b) The mixture’s reflectance is far below the average of the pure reflectances, since absorption adds up in K/S, not in R; linear unmixing explains the darkness with too much blue. (c) The Kubelka–Munk unmixing stays close to the truth; the linear one keeps a bias that noise does not remove.
★★★ Calibrated evidence for an attribution. A classifier trained on tiles of authenticated paintings by an artist and by contemporaries gives scores to the tiles of a disputed painting. (a) Explain why the accuracy on random held-out tiles overstates the evidence. (b) Propose a validation and a calibration that turn the scores into a likelihood ratio for the whole painting. (c) Discuss how this ratio could be weighed with other evidence (Tummers, 2011).
Hints: (a) Tiles of one painting share support, ground, condition and photography: the classifier can learn the painting, not the hand. (b) Leave one painting out; aggregate the tile scores per painting; fit the score distributions under both hypotheses on held-out paintings, and report, with its uncertainty, the likelihood ratio \mathrm{LR} = \frac{p(\text{score} \mid \text{artist})}{p(\text{score} \mid \text{other})} .
(c) In Bayesian terms, posterior odds are the likelihood ratio times the prior odds formed by provenance, documents and connoisseurship. Discuss workshop participation, the small number of works, and the risk of deliberately deceptive works.
★★★ Closing the gap of the Magic Eraser. (Python) In the code of ch. 6.3, the error under the ink drops tenfold, yet the watermark visibility stays far below that of the clean sheet. (a) List the causes you can test. (b) Train longer, and weight the loss on inked pixels; report the visibility over several seeds. (c) Propose a measurement that would make the separation of ink and watermark easier on real sheets.
Hints and expected result: (a) The short training, errors at the edges of strokes, and the ambiguity between thin ink and thin paper.
(b) In a test run, 600 steps instead of 150 raised the visibility from 1.4 to about 1.9, with an error of 0.054 under the ink; weighting the inked pixels five times gave about 2.1 and 0.046. The clean sheet is at 4.6. Report means and spreads over at least five seeds.
(c) A second image in which the ink is more transparent, for instance in the infrared, where iron-gall inks are known to fade, or a raking-light image that shows the texture of the paper; then feed all channels to the network.
Projects and assessment
The official assessment is by projects, submitted as code and documented and demonstrated in Jupyter notebooks (master-mva.com). Université Paris-Saclay describes it as “Evaluation Continue non Intégrale”; no weights are displayed. The subjects below follow that format: a notebook that runs, explains and demonstrates, with a short report and a talk. Each reproduces or extends published work, in the MVA tradition. No written exam is announced; the exam proposal is for self-assessment.
Solo projects
- [Solo · ≈ 20 h] Paper study: MAGSAC++ (Barath et al., 2020) against tuned RANSAC. Does marginalising over the noise scale beat a tuned threshold when the noise of correct matches varies across the image?
- Implement the normalised DLT, RANSAC, the simplified weight of Proposition 2.7 and the MAGSAC++ weights of the paper, with incomplete gamma functions.
- Data: synthetic matches with radial distortion and noise growing towards the corners; optionally, image pairs of the public HPatches benchmark (Balntas et al., 2017).
- Success criterion: grid reprojection error within 10 % of an oracle fit on the true inliers, over 20 seeds and all thresholds tried. Deliverables: notebook, 4–6 page report, 10-minute talk. Chapters 0 and 2.
- [Solo · ≈ 20 h] Paper study: style as a distribution (Gatys et al., 2016; Li et al., 2017). Which feature statistics reproduce the brushwork of a painting?
- Synthesise textures from crops of public-domain paintings (open-access images of the Rijksmuseum or the Metropolitan Museum of Art) with pretrained VGG16 features from torchvision.
- Compare Gram matching on several layer sets, matching of channel means and variances only, and random features (Ustyuzhaninov et al., 2017); verify Proposition 6.3 numerically.
- Success criterion: a figure per method, and an MMD on held-out VGG features ranking the methods. Deliverables: notebook, report, talk. Chapters 5.3 and 6.2.
- [Solo · ≈ 20 h] Paper study: weave analysis from radiographs (Johnson, Johnson and Erdmann, 2013), on simulated canvases. How long must the common part of two canvases be for a reliable match?
- Extend the simulator of ch. 4.2 with thread-thickness variations, a slight skew of the weave and stronger noise.
- Implement thread counting by tile spectra, warp matching over shifts and flips, and a null from 500 unrelated rolls.
- Success criterion: ROC curves of same-roll detection against the overlap length, and the overlap needed for a 90 % detection rate at a 1 % false-match rate. Deliverables: notebook, report, talk. Chapters 1.4 and 4.2.
Group projects
- [Group of 2–3 · ≈ 30 h each] A multimodal registration pipeline, end to end. Simulate a stack from an open-access image of a painting: an IRR-like channel with a synthetic underdrawing, element-like maps with Poisson noise, each modality with its own homography, lens distortion and smooth local warp.
- Member 1: keypoints, descriptors (handcrafted, or HardNet through the Kornia library of Riba et al., 2020), matching, robust homography with distortion (ch. 2.1–2.4).
- Member 2: patchwise offsets with tile stacks, structure-tensor weights and learned filters (Definition 2.11).
- Member 3: conditional surprisal on the registered stack (ch. 3.1), as a function of the residual misregistration.
- Success criterion: a median residual below 0.3 px, and a curve of underdrawing detection (AUC) against registration error. Deliverables: notebooks, a 6–8 page report, a 20-minute talk. Chapters 1–3.
- [Group of 2–3 · ≈ 30 h each] Paper study: CLIP (Radford et al., 2021) and DINOv2 (Oquab et al., 2024) on an open-access collection. Download a few thousand public-domain images with their metadata from the Metropolitan Museum of Art or the Rijksmuseum, and run the pretrained models on a free GPU tier.
- Member 1: a search engine with an approximate nearest-neighbour index; precision at 10 on queries built from the metadata.
- Member 2: semantic directions (Definition 5.3) tested against dates and techniques of the metadata, with an analysis of failures.
- Member 3: DINOv2 correspondences (Definition 5.4) between works and prints or copies after them, and PCA maps.
- Success criterion: precision at 10 above a keyword baseline, and a hand-checked set of correspondences with its error rate. Deliverables: notebooks, report, talk. Chapter 5.
- [Group of 2–3 · ≈ 30 h each] Paper study: U-Net (Ronneberger et al., 2015) for ink removal, after the Magic Eraser case. Can a watermark hidden under a drawing be identified after erasing the ink?
- Member 1: a simulator of sheets in transmitted light (laid and chain lines, 100 synthetic watermark designs, inks with spectral signatures).
- Member 2: the U-Net with a non-negative ink density, trained with the ablations of Exercise 19.
- Member 3: watermark retrieval from the synthetic repertory, and conformal error bars (Proposition 7.6) on held-out sheets.
- Success criterion: top-1 retrieval of the right watermark under the ink above 80 %. Deliverables: notebooks, report, talk. Chapters 1, 6 and 7.
Written exam (proposal)
Two hours, one handwritten A4 sheet and a calculator allowed; four questions of 5 points.
Question (5 points). For visible photography, infrared reflectography, radiography and MA-XRF, state the physical quantity measured, the depth reached and one question about a painting that each modality helps answer. Then explain, with a model, why an underdrawing appears in the infrared. Expected: the table of ch. 1 (reflectance at the surface; reflectance below paint; X-ray transmission dominated by lead white; element fluorescence attenuated with depth), and Proposition 1.4 with a transmittance that grows with the wavelength over a carbon-based drawing.
Question (5 points). (a) A third of the matches are correct: how many RANSAC draws give 99 % confidence for a homography? (b) With f = 6000 px and k_1 = -0.01, how far is a point at r = 0.5 displaced? (c) A 128-pixel tile at 0.05 mm per pixel, zero-padded to 1024 points, has its spectral peak at index 72: give the thread density.
Expected: (a) \log 0.01 / \log(1 - 3^{-4}) \approx 370.7, so 371 draws. (b) 0.01 \times 0.5^3 \times 6000 = 7.5 px. (c) 72/1024 = 0.0703 cycles per pixel, divided by 0.005 cm: about 14.1 threads per cm.
Question (5 points). Prove Proposition 2.10 from the shift theorem. Then derive the normal equations of Proposition 2.8, and explain why a tile containing a single straight edge cannot be registered in both directions. Expected: the cross-power spectrum reduces to a pure phase whose inverse DFT is a Kronecker delta; linearisation of A(x - \delta), the system J\delta = -\sum \nabla A\,(B - A), and a rank-one structure tensor when all gradients are parallel.
Question (5 points). Prove Proposition 6.3, and deduce that two images whose features are the same up to a permutation of positions have the same style loss. What does this imply for the transfer of style to a painting with a given composition? Expected: \langle a a^\top, b b^\top\rangle_F = (a^\top b)^2, expansion into the three kernel sums, invariance of \sum_m f_m f_m^\top to permutations; style is a distribution of local features, so composition must come from the content loss.
Grading rubric (proposal)
The official grade comes from the projects, in the form of code and notebooks. The weights below are a proposal for grading one project.
| Criterion | Weight | What earns the marks |
|---|---|---|
| Question and positioning | 15 % | A precise question, the paper it reproduces or extends, and why it matters for heritage science |
| Correctness of methods and code | 25 % | Correct mathematics, correct implementation, tests against known cases (synthetic ground truth) |
| Experiments and evaluation | 20 % | Baselines, several seeds, error bars, success criteria fixed in advance, validation on separate image blocks |
| Notebook: documentation and demonstration | 15 % | A notebook that runs from top to bottom, explains each step and demonstrates the result, as the official assessment requires |
| Critical discussion | 15 % | The physical assumptions of the data or simulation, the limits of the evidence, what a curator or art historian may and may not conclude |
| Presentation and questions | 10 % | A clear talk within time, and precise answers |
References
Official sources and readings of the course
- master-mva.com, course page “Artificial Intelligence and Computer Vision for Cultural Heritage”, 2026–2027: https://www.master-mva.com/cours/ai-and-computer-vision-for-cultural-heritage/.
- Université Paris-Saclay, M2 Mathematics, Vision, Learning, programme table and course detail “AI and Computer vision for cultural heritage” (ECTS, hourly volume, form of assessment).
- A. Tummers, The Eye of the Connoisseur: Authenticating Paintings by Rembrandt and His Contemporaries, J. Paul Getty Museum, 2011 (additional reading named by the course).
- A. Barroso-Laguna, E. Riba, D. Ponsa, K. Mikolajczyk, “Key.Net: keypoint detection by handcrafted and learned CNN filters”, ICCV, 2019.
- D. Barath, J. Noskova, M. Ivashechkin, J. Matas, “MAGSAC++, a fast, reliable and accurate robust estimator”, CVPR, 2020.
- L. A. Gatys, A. S. Ecker, M. Bethge, “Image style transfer using convolutional neural networks”, CVPR, 2016.
- O. Ronneberger, P. Fischer, T. Brox, “U-Net: convolutional networks for biomedical image segmentation”, MICCAI, 2015.
- A. Radford, J. W. Kim, C. Hallacy, A. Ramesh, G. Goh, S. Agarwal, G. Sastry, A. Askell, P. Mishkin, J. Clark, G. Krueger, I. Sutskever, “Learning transferable visual models from natural language supervision”, ICML, 2021.
- M. Oquab, T. Darcet, T. Moutakanni et al., “DINOv2: learning robust visual features without supervision”, Transactions on Machine Learning Research, 2024 (arXiv:2304.07193, 2023).
- D. Mishkin, F. Radenović, J. Matas, “Repeatability is not enough: learning affine regions via discriminability”, ECCV, 2018 (AffNet, named in the syllabus).
- A. Mishchuk, D. Mishkin, F. Radenović, J. Matas, “Working hard to know your neighbor’s margins: local descriptor learning loss”, NeurIPS, 2017 (HardNet, named in the syllabus).
- K. Simonyan, A. Zisserman, “Very deep convolutional networks for large-scale image recognition”, ICLR, 2015 (VGG16, named in the syllabus).
Textbooks
- R. Hartley, A. Zisserman, Multiple View Geometry in Computer Vision, 2nd ed., Cambridge University Press, 2004.
- R. Szeliski, Computer Vision: Algorithms and Applications, 2nd ed., Springer, 2022.
- T. Lindeberg, Scale-Space Theory in Computer Vision, Kluwer Academic Publishers, 1994.
- B. W. Silverman, Density Estimation for Statistics and Data Analysis, Chapman and Hall, 1986.
- C. M. Bishop, Pattern Recognition and Machine Learning, Springer, 2006.
- I. Goodfellow, Y. Bengio, A. Courville, Deep Learning, MIT Press, 2016.
- V. Vovk, A. Gammerman, G. Shafer, Algorithmic Learning in a Random World, Springer, 2005.
Imaging of paintings and works on paper (ch. 1, 3, 4, 6, 7)
- P. Kubelka, F. Munk, “Ein Beitrag zur Optik der Farbanstriche”, Zeitschrift für technische Physik 12, 593–601, 1931.
- J. R. J. van Asperen de Boer, “Reflectography of paintings using an infrared vidicon television system”, Studies in Conservation 14(3), 96–118, 1969.
- J. K. Delaney, J. G. Zeibel, M. Thoury, R. Littleton, M. Palmer, K. M. Morales, E. R. de la Rie, A. Hoenigswald, “Visible and infrared imaging spectroscopy of Picasso’s Harlequin Musician: mapping and identification of artist materials in situ”, Applied Spectroscopy 64(6), 584–594, 2010.
- J. M. Bioucas-Dias, A. Plaza, N. Dobigeon, M. Parente, Q. Du, P. Gader, J. Chanussot, “Hyperspectral unmixing overview: geometrical, statistical, and sparse regression-based approaches”, IEEE Journal of Selected Topics in Applied Earth Observations and Remote Sensing 5(2), 354–379, 2012.
- J. Dik, K. Janssens, G. Van der Snickt, L. van der Loeff, K. Rickers, M. Cotte, “Visualization of a lost painting by Vincent van Gogh using synchrotron radiation based X-ray fluorescence elemental mapping”, Analytical Chemistry 80(16), 6436–6442, 2008.
- M. Alfeld, K. Janssens, J. Dik, W. de Nolf, G. van der Snickt, “Optimization of mobile scanning macro-XRF systems for the in situ investigation of historical paintings”, Journal of Analytical Atomic Spectrometry 26, 899–909, 2011.
- J. Geng, “Structured-light 3D surface imaging: a tutorial”, Advances in Optics and Photonics 3(2), 128–160, 2011.
- C. Zuo, S. Feng, L. Huang, T. Tao, W. Yin, Q. Chen, “Phase shifting algorithms for fringe projection profilometry: a review”, Optics and Lasers in Engineering 109, 23–59, 2018.
- S. Bucklow, “The description of craquelure patterns”, Studies in Conservation 42(3), 129–140, 1997.
- B. Cornelis, T. Ružić, E. Gezels, A. Dooms, A. Pižurica, L. Platiša, J. Cornelis, M. Martens, M. De Mey, I. Daubechies, “Crack detection and inpainting for virtual restoration of paintings: the case of the Ghent Altarpiece”, Signal Processing 93(3), 605–619, 2013.
- D. H. Johnson, C. R. Johnson Jr., R. G. Erdmann, “Weave analysis of paintings on canvas from radiographs”, Signal Processing 93(3), 527–540, 2013.
- C. R. Johnson Jr., E. Hendriks, I. J. Berezhnoy, E. Brevdo, S. M. Hughes, I. Daubechies, J. Li, E. Postma, J. Z. Wang, “Image processing for artist identification”, IEEE Signal Processing Magazine 25(4), 37–48, 2008.
- C.-M. Briquet, Les Filigranes: dictionnaire historique des marques du papier dès leur apparition vers 1282 jusqu’en 1600, Geneva, 1907.
Registration and local features (ch. 2, 4, 7)
- C. Harris, M. Stephens, “A combined corner and edge detector”, Alvey Vision Conference, 1988.
- D. G. Lowe, “Distinctive image features from scale-invariant keypoints”, International Journal of Computer Vision 60(2), 91–110, 2004.
- T. Lindeberg, “Edge detection and ridge detection with automatic scale selection”, International Journal of Computer Vision 30(2), 1998.
- A. F. Frangi, W. J. Niessen, K. L. Vincken, M. A. Viergever, “Multiscale vessel enhancement filtering”, MICCAI, 1998.
- M. A. Fischler, R. C. Bolles, “Random sample consensus: a paradigm for model fitting with applications to image analysis and automated cartography”, Communications of the ACM 24(6), 381–395, 1981.
- D. Barath, J. Matas, J. Noskova, “MAGSAC: marginalizing sample consensus”, CVPR, 2019.
- R. I. Hartley, “In defense of the eight-point algorithm”, IEEE Transactions on Pattern Analysis and Machine Intelligence 19(6), 580–593, 1997.
- Z. Zhang, “A flexible new technique for camera calibration”, IEEE Transactions on Pattern Analysis and Machine Intelligence 22(11), 1330–1334, 2000.
- B. D. Lucas, T. Kanade, “An iterative image registration technique with an application to stereo vision”, IJCAI, 1981.
- C. D. Kuglin, D. C. Hines, “The phase correlation image alignment method”, Proceedings of the IEEE International Conference on Cybernetics and Society, 1975.
- M. Guizar-Sicairos, S. T. Thurman, J. R. Fienup, “Efficient subpixel image registration algorithms”, Optics Letters 33(2), 156–158, 2008.
- P. Viola, W. M. Wells III, “Alignment by maximization of mutual information”, International Journal of Computer Vision 24(2), 137–154, 1997.
- F. Maes, A. Collignon, D. Vandermeulen, G. Marchal, P. Suetens, “Multimodality image registration by maximization of mutual information”, IEEE Transactions on Medical Imaging 16(2), 187–198, 1997.
- V. Balntas, K. Lenc, A. Vedaldi, K. Mikolajczyk, “HPatches: a benchmark and evaluation of handcrafted and learned local descriptors”, CVPR, 2017.
- E. Riba, D. Mishkin, D. Ponsa, E. Rublee, G. Bradski, “Kornia: an open source differentiable computer vision library for PyTorch”, WACV, 2020.
Embeddings, style and image-to-image networks (ch. 5–7)
- A. van den Oord, Y. Li, O. Vinyals, “Representation learning with contrastive predictive coding”, arXiv:1807.03748, 2018.
- B. Poole, S. Ozair, A. van den Oord, A. A. Alemi, G. Tucker, “On variational bounds of mutual information”, ICML, 2019.
- M. Caron, H. Touvron, I. Misra, H. Jégou, J. Mairal, P. Bojanowski, A. Joulin, “Emerging properties in self-supervised vision transformers”, ICCV, 2021.
- A. Dosovitskiy et al., “An image is worth 16x16 words: transformers for image recognition at scale”, ICLR, 2021.
- J. Johnson, M. Douze, H. Jégou, “Billion-scale similarity search with GPUs”, IEEE Transactions on Big Data 7(3), 535–547, 2021.
- L. A. Gatys, A. S. Ecker, M. Bethge, “A neural algorithm of artistic style”, arXiv:1508.06576, 2015.
- Y. Li, N. Wang, J. Liu, X. Hou, “Demystifying neural style transfer”, IJCAI, 2017.
- I. Ustyuzhaninov, W. Brendel, L. A. Gatys, M. Bethge, “What does it take to generate natural textures?”, ICLR, 2017.
- X. Huang, S. Belongie, “Arbitrary style transfer in real-time with adaptive instance normalization”, ICCV, 2017.
- J. Johnson, A. Alahi, L. Fei-Fei, “Perceptual losses for real-time style transfer and super-resolution”, ECCV, 2016.
- P. Isola, J.-Y. Zhu, T. Zhou, A. A. Efros, “Image-to-image translation with conditional adversarial networks”, CVPR, 2017.
- J. Lei, M. G’Sell, A. Rinaldo, R. J. Tibshirani, L. Wasserman, “Distribution-free predictive inference for regression”, Journal of the American Statistical Association 113(523), 1094–1111, 2018.
Research directions (ch. 8)
- J. Sun, Z. Shen, Y. Wang, H. Bao, X. Zhou, “LoFTR: detector-free local feature matching with transformers”, CVPR, 2021.
- P. Lindenberger, P.-E. Sarlin, M. Pollefeys, “LightGlue: local feature matching at light speed”, ICCV, 2023.
- J. Edstedt, Q. Sun, G. Bökman, M. Wadenbäck, M. Felsberg, “RoMa: robust dense feature matching”, CVPR, 2024.
- R. Sizyakin, B. Cornelis, L. Meeus, H. Dubois, M. Martens, V. Voronin, A. Pižurica, “Crack detection in paintings using convolutional neural networks”, IEEE Access 8, 2020.
- X. Shen, A. A. Efros, M. Aubry, “Discovering visual patterns in art collections with spatially-consistent feature learning”, CVPR, 2019.
- A. Elgammal, B. Liu, D. Kim, M. Elhoseiny, M. Mazzone, “The shape of art history in the eyes of the machine”, AAAI, 2018.
- T. Mensink, J. van Gemert, “The Rijksmuseum Challenge: museum-centered visual recognition”, ICMR, 2014.
- G. Liu, F. A. Reda, K. J. Shih, T.-C. Wang, A. Tao, B. Catanzaro, “Image inpainting for irregular holes using partial convolutions”, ECCV, 2018.
- A. Lugmayr, M. Danelljan, A. Romero, F. Yu, R. Timofte, L. Van Gool, “RePaint: inpainting using denoising diffusion probabilistic models”, CVPR, 2022.
- S. Lyu, D. Rockmore, H. Farid, “A digital technique for art authentication”, Proceedings of the National Academy of Sciences 101(49), 17006–17010, 2004.