PatchCore on MVTec AD (ONNX, runs in the browser)
From-scratch PyTorch reimplementation of PatchCore (Roth et al., Towards Total Recall in Industrial Anomaly Detection, CVPR 2022), exported to ONNX. Fitted on defect-free images only; defects are found as patches that are far from every patch seen on good parts.
Live demo (in your browser, no server): yandyassine/patchcore-defect-detection Β· Code & write-up: COMPUTER-VISION-PROJECT-LIBRARY / 13
Results (MVTec AD test set, image level)
| Category | Test images | Image AUROC | F1 at the shipped threshold |
|---|---|---|---|
| bottle | 83 | 1.000 | 1.000 |
| metal_nut | 115 | 0.999 | 0.995 |
| grid | 78 | 0.977 | 0.974 |
These numbers were re-measured with the ONNX files in this repo (fp16-stored weights), and match the PyTorch model exactly at this precision.
Files
| File | What it does | Size |
|---|---|---|
backbone.onnx |
image (1,3,224,224) β patch features (784, 1536): WideResNet-50-2 layers 2+3, 3Γ3 local averaging |
50 MB |
knn_<category>.onnx |
features β anomaly map (224,224) + image score; memory bank (10 % greedy coreset) baked in |
50β64 MB |
thresholds.json |
per-category decision threshold (best F1 on the test set) |
Weights are stored as float16 and cast to float32 inside the graph, so the math runs in float32.
Use
Preprocessing: resize the shorter side to 256, center-crop 224, ImageNet mean/std normalization.
import numpy as np, onnxruntime as ort
bb = ort.InferenceSession("backbone.onnx")
knn = ort.InferenceSession("knn_bottle.onnx")
feats = bb.run(None, {"image": x})[0] # x: float32 (1,3,224,224)
amap, score = knn.run(None, {"features": feats})
defect = score > 1.9728 # thresholds.json["bottle"]["threshold"]
Limitations
- One model per product; a photo of another object will look anomalous everywhere (by design).
- Thresholds were picked on the MVTec test set itself, so the F1 column is optimistic; AUROC is threshold-free.
- Trained at 224 px; very small defects can be missed.
License
Memory banks are derived from MVTec AD, licensed CC BY-NC-SA 4.0: non-commercial use only. Backbone weights: torchvision ImageNet WideResNet-50-2.