Surface defect inspection with a line-scan camera
Private source
The anomaly detector learns only from good samples and needs no defect images. Each image returns OK or NOK, an anomaly map, a mask and boxes around suspect regions.
Results
Results on a 133-image test set (26 defective, 107 good). The README marks the latency figures as historical results.
- Recall
- 24/26 (92.3%)
- 133 test images: 26 defective, 107 good
- False alarms
- 3/107 (2.8%)
- same test set
- P50 latency
- 43 ms
- RTX A4000, FP16, batch 15, 3,990 consecutive frames; P95 is 44 ms
- On CPU
- ≈ 683 ms
- OpenVINO FP32, 2 × Xeon Gold 6138
Problem
Defects are too rare and varied to collect for training, and the line needs a decision for every image within about 50 ms.
Approach
- PatchCore on MobileNetV3-Large (FP16), with a memory bank built from only 10 good images.
- Each 1024 × 1792 image is tiled, scored, merged into one anomaly map and thresholded on 50 separate good images.
More implementation details
- Features come from blocks.4 and blocks.6. Tiles are 448 px with 0.20 overlap; the memory bank uses a 0.10 coreset.
- Tile scores are blended with a Hann window, Gaussian-smoothed, normalized against good-image statistics and restricted to the ROI.
- An operations dashboard built with aiohttp and React 19 (TypeScript, Vite, shadcn/ui) keeps history in SQLite and grabs frames from a Basler camera via pypylon. A Train & Setup tab lets operators create a product, upload good images, train and activate it from the browser.
Deployment lessons
- 1.An encoder-triggered line-scan camera only produces frames while the table turns. No frames while the motor is stopped is correct behaviour, not a network fault.
- 2.A camera cable routed through a PLC port runs at only 100 Mbps. Around 64 Mbit/s was fine; pushing the table speed toward 98 Mbit/s started dropping frames.
How it was evaluated
Few-shot experiments under a budget of 10 or 15 good images were run across 8 seeds with nested leave-one-out validation, giving 160 operating points.
Conclusions are recorded even when a new configuration does not help, for example: no configuration yet improves recall and the false-alarm rate at the same time. Keeping negative results stops the next attempt from repeating a dead end.
Stack
- PyTorch
- PatchCore
- MobileNetV3
- FP16
- OpenVINO
- aiohttp
- React 19
- TypeScript
- SQLite
- pypylon


