Research question. How well does a crack classifier trained on one infrastructure surface (bridge decks) generalize to unseen surface types (walls, pavements), and does it rely on genuine crack morphology rather than spurious visual cues (shadows, edges, texture transitions, stains)?
This is the first of two testbeds in a shared line of work, Trustworthy Vision under Distribution Shift:
| Testbed | Task | Distribution shift |
|---|---|---|
| This repo | crack classification | surface type (bridge → wall / pavement) |
| Flood segmentation (separate repo) | flood segmentation | geography (region → unseen region) |
Both ask the same question: does a vision model stay reliable — and for the right reasons — when the test data differs from what it trained on?
SDNET2018 is ~56k labelled 256×256 patches of concrete surfaces, split into three top-level folders after extraction:
data/SDNET2018/
├── D/ bridge decks → CD/ (cracked) UD/ (uncracked)
├── P/ pavements → CP/ UP/
└── W/ walls → CW/ UW/
Download it from Kaggle (search "SDNET2018") or Mendeley Data / Utah State
University DigitalCommons, unzip into data/, and confirm the D/ P/ W/ layout
above. The data folder is git-ignored — do not commit it.
Note on imbalance: bridge decks are only ~15% cracked, so we report F1, recall and PR-AUC, not just accuracy, and train with class weighting.
pip install -r requirements.txtA GPU helps but is optional. On Google Colab, enable a GPU runtime, clone this repo, and run the notebooks in order.
| Notebook | What it does |
|---|---|
01_data_exploration.ipynb |
Load each domain, show class counts + sample images, confirm the imbalance. |
02_train_resnet18.ipynb |
Train an ImageNet-pretrained ResNet18 on bridge deck only; checkpoint the best epoch by val F1. |
03_cross_domain_evaluation.ipynb |
Freeze a decision threshold on source validation, then evaluate on bridge-test + wall + pavement; build the results table and the generalization gap. |
04_gradcam_analysis.ipynb |
Bucket predictions (correct-crack / correct-non-crack / FP / FN) and inspect Grad-CAM heatmaps; compute the mask-free edge-intensity heuristic. |
Wall and pavement are never touched during training, tuning, or threshold selection. The decision threshold is chosen once on bridge-deck validation and reused unchanged on every target domain. Any tuning on target data would leak information and inflate the reported generalization — so we don't.
cross-domain-crack-classification/
├── README.md
├── requirements.txt
├── notebooks/ 01–04, run in order
├── src/ datasets.py · train.py · evaluate.py · gradcam_utils.py
├── configs/ default.yaml
├── figures/ exported Grad-CAM grids, plots
├── results/ metric tables (csv/json)
└── models/ checkpoints (git-ignored)
Setup. ResNet18 (ImageNet-pretrained) trained on bridge-deck patches only, with class weighting for the ~15% crack rate. The decision threshold (0.55) was selected on the source-validation split and then frozen; walls and pavements were never seen during training, tuning, or threshold selection.
| Test domain | Accuracy | F1 | ROC-AUC | PR-AUC | Gen. gap (F1) |
|---|---|---|---|---|---|
| Bridge deck (in-domain test) | 0.921 | 0.713 | 0.915 | 0.798 | — |
| Wall (unseen) | 0.888 | 0.711 | 0.881 | 0.749 | −0.002 |
| Pavement (unseen) | 0.910 | 0.508 | 0.756 | 0.526 | −0.205 |
Generalization is asymmetric. The bridge-trained model transfers almost perfectly to walls (F1 gap −0.002) but degrades sharply on pavements (F1 gap −0.205). Bridge decks and walls are both concrete with similar texture, while pavement (asphalt) differs in surface and crack morphology — so reliability tracks how close the target surface is to the training surface, rather than being a fixed property of the model.
The pavement drop is a genuine loss of discrimination, not a threshold artifact. F1 depends on the frozen 0.55 threshold, so we check the threshold-independent metrics: ROC-AUC falls 0.915 → 0.756 and PR-AUC 0.798 → 0.526. Both drop, so the model's ability to separate cracks from non-cracks genuinely weakens off-domain — something a per-domain threshold would not fix. On walls these metrics are largely preserved, confirming true transfer.
On walls the model produces 0 false positives and 138 false negatives (vs. 278 correctly-detected cracks): off-domain it becomes conservative — it rarely raises a false alarm but misses real cracks. Precision is preserved while recall drops. Grad-CAM overlays support this: on correct detections the activation sits on the crack line; on missed cracks the heat drifts to borders (edge-intensity 0.38–0.54) or collapses on near-featureless patches (edge 0.00).
Limitation. With zero false positives on walls, the "spurious-cue" hypothesis (whether false alarms are driven by frame/surface edges) cannot yet be tested here — the current evidence speaks to recall loss and asymmetric transfer, not spurious cues. A domain with more false positives, or a segmentation dataset with pixel-level crack masks, would be needed to probe that directly.
Crack testbed complete: trained model, cross-domain evaluation, and Grad-CAM analysis with the results above. Next testbed: cross-region flood segmentation (Sen1Floods11), reusing the same evaluation protocol on a geographic distribution shift.