Back to Knowledge Base
PatchCore DINOv2 Anomaly Detection Deep Learning

No Defect Samples Needed: How PatchCore Detects Anomalies

PatchCore flips the defect-detection problem on its head โ€” instead of learning what defects look like, it memorises what normal looks like. Here's the math behind it.

April 18, 20263 min read

The biggest bottleneck in industrial defect detection isn't the algorithm โ€” it's the data. Defects are rare by design, so you can spend months on a production line collecting enough labelled defect images to train a classifier.

PatchCore sidesteps this entirely. You only need images of good parts.

The Core Idea

Traditional supervised detection learns: "this texture = scratch, this shape = dent."

PatchCore asks instead: "how far is this image from everything we've seen that's normal?"

Training (normal images only):
  Good images โ†’ Feature extraction โ†’ Memory Bank

Inference:
  Test image โ†’ Feature extraction โ†’ Distance to Memory Bank
                                           โ†“
                                   Small โ†’ Normal
                                   Large โ†’ Anomaly (+ heatmap)

If a test patch is far from every normal patch in the Memory Bank, it's anomalous. No defect label needed.

Why DINOv2 Instead of ResNet?

PatchCore's original paper uses ImageNet-pretrained ResNet features. VisionLab uses DINOv2-S โ€” a Vision Transformer trained with self-supervised learning on 142 M images.

DINOv2's patch tokens have a key property: spatial semantics are naturally separated. Patches of smooth metal cluster together; patches with a scratch cluster away. This makes the nearest-neighbour distance signal cleaner, reducing false positives on parts with complex textures.

For a 224ร—224 input, DINOv2-S produces a [256, 384] feature map โ€” 256 spatial positions, each with a 384-dimensional descriptor.

Coreset Sampling: Keeping the Memory Bank Small

Ten training images produce ~2,560 patch feature vectors. Storing all of them makes inference O(query ร— 2560) per patch โ€” too slow for production.

Greedy coreset sampling selects a representative subset SโІFS \subseteq \mathcal{F} of size K=0.1ร—โˆฃFโˆฃK = 0.1 \times |\mathcal{F}|:

Sโˆ—=argโกminโกS,โ€‰โˆฃSโˆฃ=KmaxโกfโˆˆFminโกsโˆˆSโˆฅfโˆ’sโˆฅ2S^* = \arg\min_{S,\, |S|=K} \max_{f \in \mathcal{F}} \min_{s \in S} \|f - s\|_2

The algorithm greedily picks the point farthest from all already-selected points at each step. The resulting 256 vectors cover the original feature space almost as well as all 2,560 โ€” with 10ร— faster inference.

Anomaly Score per Patch

At inference, for each query patch qq:

score(q)=minโกmโˆˆM(1โˆ’qโ‹…mโˆฅqโˆฅโ‹…โˆฅmโˆฅ)\text{score}(q) = \min_{m \in \mathcal{M}} \left(1 - \frac{q \cdot m}{\|q\| \cdot \|m\|}\right)

Cosine distance is used instead of Euclidean because DINOv2 features lie on a hypersphere โ€” cosine distance is more stable across feature magnitude variations.

The per-patch scores form a spatial map that is then upsampled to the original image resolution and Gaussian-smoothed, producing a heatmap that highlights exactly where the anomaly is located.

How Few Images Are Really Enough?

Training imagesAUROC (metal casting dataset)
391.2%
594.8%
897.4%
2098.1%

With 5โ€“10 good-part images you get production-viable accuracy. Coverage matters more than quantity โ€” make sure the training set shows the part under all lighting and pose variations you expect at runtime.

Running PatchCore in VisionLab

Training and Memory Bank construction are done once via a Python script:

python export_transformer.py          # export DINOv2 backbone โ†’ dinov2_vits14.pt
python train_defect_transformer.py    # build memory bank โ†’ part_memory_bank.pt

At runtime, the C++ plugin loads the .pt files with LibTorch and serves inference results over IPC to your host application โ€” no Python runtime needed in production.