Universal Defect Detection for Watch Components
Spring 2025
I built a defect detector that learns what a good watch part looks like from a few dozen images. A new component no longer needs months of defect labeling before inspection can be automated.
- Defect labels needed to start on a new component
- 0
- Defect labels needed to start on a new component
- Images are enough to get a useful detector
- 20–50
- Images are enough to get a useful detector
- Backbones benchmarked: CNN, DINOv2, RADIO
- 3
- Backbones benchmarked: CNN, DINOv2, RADIO
01 · The problem
Every new component meant a new labeled dataset
Watch components must be aesthetically perfect, so they are inspected by hand. Automating that with a classic classifier means collecting and labeling OK and defective examples again for every new part.
Supervised classifier
A new dataset for every part
Repeat for each component. Months every time
Defects are rare and hard to label consistently. When the model underperforms, data scientists and factory staff have to dig through the labels together.
Anomaly detection
One recipe for any part
No defect labels. Much faster to start
The model learns what normal looks like, mostly from acceptable pieces. The factory can set it up on a new component without a data science team in the loop.
- Manual inspection is slow
- Borderline defects split even expert inspectors
- The imaging hardware was already in place
02 · The idea
Learn what normal looks like. Flag what is far from it
Instead of teaching a model every possible defect, show it good parts only. Anything that doesn't look like what it has seen gets a high anomaly score.
- 1
Cut. Each image is split into small patches.
- 2
Embed. A pretrained backbone turns every patch into a feature vector.
- 3
Remember. Features from good parts are stored in a memory bank.
- 4
Compare. A new patch far from everything in the bank is flagged.
Hover or tap a patch to see how far it is from the closest normal patch in the memory bank.
The score of the whole image is its worst patch, here 0.91.
I also explored teacher-student models: a pretrained teacher such as DINOv2 and a small student trained on good parts only. Where the two disagree, something is unusual.
03 · The catch
The "clean" training set wasn't clean
The main issue I found was in the data, not the model. The training images labeled OK contained some defects. A memory bank memorizes them and then treats the same defect as normal.
Anomaly score of the new patch0.06
Its closest neighbor is a defect the bank memorized. The defect passes as normal.
- 1.Extract patch features from all training images
- 2.Score how isolated each feature is (Local Outlier Factor or a multivariate Gaussian)
- 3.Remove or down-weight the outliers
- 4.Build the memory bank from what is left
The key assumption, borrowed from SoftPatch: defects are rare. So a feature with few close neighbors is more likely a hidden defect than a normal pattern.
04 · Results
A few dozen images, no labels, clear separation
The metric: place the threshold so that only 5% of defective pieces slip through, then count how many good pieces are accepted with no human look.
- 5%
- of defective pieces slip through
- 85%
- of good pieces need no human look
Exact figures are confidential. From memory, with only 20 to 50 unlabeled images of a component, the scores of good and defective pieces separated cleanly, and at the 5% operating point roughly 80% or more of good pieces passed automatically.
- Tested on several components from several brands
- No defect labeling needed for a new component
- Live demo to stakeholders across several brands
05 · Speed vs. detail
More detail isn't free
The detector also had to be fast enough for the inspection workflow. I tested resizing the image to several sizes, and cutting it into a grid of cells with one memory bank per cell.
Memory banks: 1
- Detail per defectLow
- Compute costLow
- GeneralizationHigh
One memory bank for the whole part. Fastest, but small defects cover few pixels.
Qualitative trend, not measurements.
CNN
The classic choice for memory-bank methods.
DINOv2
Self-supervised vision transformer with 16 × 16 pixel patches.
RADIO
NVIDIA's vision foundation model.
Results varied by component, so there was no single winner. Every run was tracked in MLflow to compare detection quality against inference latency.
06 · In production
A small service next to the machine
The acquisition machine does not run the model. A separate computer with a GPU sits next to it and exposes the detector as a small web service.
Acquisition machine
Takes aligned, evenly lit images of each piece
GPU computer
Docker container
Inference runs locally, next to the line
{"anomaly_score": 0.82,"prediction": "KO"}
Score and decision in a validated, structured format (illustrative)
Docker
Model code and dependencies ship as one image, so the setup is reproducible.
Pydantic
Inputs and outputs are validated, so the contract with the acquisition software is explicit.
MLflow
Each candidate was benchmarked on accuracy and latency before deployment.
07 · Takeaways
What I'd tell the next team
01
Check the "clean" data first
The biggest gain came from questioning the training set. A method that tolerates a few hidden defects beats one that assumes perfect data.
02
Ease of use was the requirement
The brands already had cameras. What they lacked was a model that works on a new part without a data science team in the loop.
03
Agree on the operating point
A score means little on its own. Fixing the share of defects allowed through made results comparable across components.