GerberFormer: Design-Conditioned PCB Defect Detection

TL;DR

Image-only PCB defect detectors fail catastrophically on unseen board families (mAP@50: 0.06). Adding a synthetic Gerber prior closes this gap completely (mAP@50: 0.99) with zero inference overhead.

Model Description

GerberFormer addresses a fundamental weakness in automated optical inspection (AOI): standard detectors memorize the visual appearance of specific PCB board families rather than learning what defects fundamentally are. When deployed on a new board family never seen during training, performance collapses.

Our solution: blend a synthetic design prior (derived from median board images using CLAHE enhancement and adaptive thresholding) into the input image at a controlled strength alpha=0.25. This gives the model structural context about what the board should look like, enabling it to detect deviations regardless of board identity.

Key Results

Model Seen Boards mAP@50 Unseen Boards mAP@50
YOLOv8s image-only (baseline) 0.9924 0.0588
YOLOv8s + Gerber alpha=0.10 0.9902 0.9890
YOLOv8s + Gerber alpha=0.25 (ours) 0.9916 0.9928
YOLOv8s + Gerber alpha=0.50 0.9817 0.9821

The generalization gap closes from 0.934 to 0.001.

Per-Class Performance (Test Set)

Defect Class Baseline AP@50 Gerber AP@50
missing_hole 0.9943 0.9937
mouse_bite 0.9949 0.9949
open_circuit 0.9941 0.9937
short 0.9923 0.9901
spurious_copper 0.9950 0.9950
spur 0.9871 0.9908

Inference Speed

No overhead from Gerber conditioning.

Model Preprocess Inference Total
Baseline 0.96ms 1.44ms 3.39ms
Gerber (ours) 0.79ms 1.26ms 2.87ms

Models in this Repository

File Description Val mAP@50
yolov8_baseline.pt YOLOv8s image-only baseline, 100 epochs 0.992
yolov8_gerber_alpha025.pt Main model, Gerber fusion alpha=0.25, 100 epochs 0.992
yolov8_gerber_alpha010.pt Ablation, Gerber fusion alpha=0.10, 50 epochs 0.992
yolov8_gerber_alpha050.pt Ablation, Gerber fusion alpha=0.50, 50 epochs 0.981

Usage

Quick Start

from huggingface_hub import hf_hub_download
from ultralytics import YOLO

path = hf_hub_download(
    repo_id  = "pulipakav-1/gerberformer",
    filename = "yolov8_gerber_alpha025.pt"
)
model   = YOLO(path)
results = model.predict("your_pcb_image.jpg", conf=0.3)
results[0].show()

With Gerber Prior (recommended for unseen boards)

from huggingface_hub import hf_hub_download
from ultralytics import YOLO
import cv2, numpy as np

model_path = hf_hub_download(
    repo_id  = "pulipakav-1/gerberformer",
    filename = "yolov8_gerber_alpha025.pt"
)

# Load your board image and prior
img   = cv2.imread("your_pcb_image.jpg")
img   = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img   = cv2.resize(img, (640, 640))
prior = cv2.imread("your_board_prior.png")
prior = cv2.cvtColor(prior, cv2.COLOR_BGR2RGB)
prior = cv2.resize(prior, (640, 640))

# Blend with alpha=0.25
fused = cv2.addWeighted(
    img.astype(np.float32),   0.75,
    prior.astype(np.float32), 0.25, 0
).astype(np.uint8)
cv2.imwrite("fused_input.jpg", cv2.cvtColor(fused, cv2.COLOR_RGB2BGR))

# Inference
model   = YOLO(model_path)
results = model.predict("fused_input.jpg", conf=0.3)
results[0].show()

Build Your Own Gerber Prior

import cv2, numpy as np
from pathlib import Path

def build_gerber_prior(image_dir, n_samples=100, img_size=640):
    images = sorted(Path(image_dir).glob("*.jpg"))[:n_samples]
    stack  = []
    for p in images:
        img = cv2.imread(str(p))
        img = cv2.resize(img, (img_size, img_size))
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        stack.append(img.astype(np.float32))
    median   = np.median(np.stack(stack), axis=0).astype(np.uint8)
    gray     = cv2.cvtColor(median, cv2.COLOR_RGB2GRAY)
    clahe    = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
    enhanced = clahe.apply(gray)
    adaptive = cv2.adaptiveThreshold(
        enhanced, 255,
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY, 21, 5)
    sobelx   = cv2.Sobel(enhanced, cv2.CV_64F, 1, 0, ksize=3)
    sobely   = cv2.Sobel(enhanced, cv2.CV_64F, 0, 1, ksize=3)
    gradient = np.sqrt(sobelx**2 + sobely**2)
    gradient = cv2.normalize(gradient, None, 0, 255,
                              cv2.NORM_MINMAX).astype(np.uint8)
    return np.stack([enhanced, adaptive, gradient], axis=-1)

prior = build_gerber_prior("path/to/your_board_images/")
cv2.imwrite("my_board_prior.png", prior)

Dataset

PKU PCB Defect Dataset

Reproduce from Scratch

git clone https://github.com/pulipakav-1/gerberformer
cd gerberformer
pip install -r requirements.txt

kaggle datasets download -d norbertelter/pcb-defect-dataset
unzip pcb-defect-dataset.zip -d pcb-defect-dataset

python priors/build_priors.py
python priors/build_fused_dataset.py
python training/train_gerber.py

python evaluation/evaluate_per_family.py \
    --model experiments/gerber_alpha025/weights/best.pt \
    --imgdir pcb-fused-dataset/test/images \
    --lbldir pcb-defect-dataset/test/labels

Limitations

  1. Requires multiple training images from the same board family to build a reliable prior
  2. Uses appearance-based priors rather than true Gerber CAD files
  3. Alpha blending is a simple fusion — learned fusion could do better
  4. Prior quality degrades for board families with very few training images

Future Work

  • Replace synthetic priors with real Gerber file rendering
  • Implement learned cross-modal fusion via cross-attention
  • Extend to multi-layer PCB inspection
  • Deploy with ONNX/TensorRT for real-time AOI

Links

Citation

@article{gerberformer2025,
  title={Design-Conditioned PCB Defect Detection via Synthetic Gerber Priors},
  author={Venkata Naga Sai Vishnu Rohit Pulipaka},
  journal={Under Review},
  year={2025}
}

License

MIT

Downloads last month
26
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support