AI-powered deep learning for automatic crack detection in concrete structures
| π Live Demo | π¦ Model Info | π Quick Start | π Results |
|---|---|---|---|
| view | view | view | view |
A high-performance UNet-based deep learning model for detecting and segmenting cracks in concrete images. Trained on 800 annotated images with 25 epochs, achieving exceptional accuracy metrics.
Perfect for: Infrastructure inspection, bridge monitoring, building assessment, quality control.
| Metric | Score |
|---|---|
| Dice Score | 99.77% |
| IoU Score | 99.54% |
| F1 Score | 99.77% |
| Precision | 99.54% |
| Recall | 100.00% |
| Training Loss | 0.0162 |
| Validation Loss | 0.0139 |
β
High-Performance UNet - 4-level encoder-decoder with BatchNorm and Dropout
β
Automatic Mask Inversion - Detects and handles inverted masks automatically
β
8-Type Data Augmentation - Robust training with diverse transformations
β
Combined Loss Function - 60% Dice + 40% BCE for optimal segmentation
β
Production Ready - Easy inference with single function call
β
Comprehensive Metrics - Dice, IoU, F1, Precision, Recall validation
β
Well Documented - Complete notebook + inference script + model card
Try the interactive Gradio web interface: π Launch Demo on Hugging Face Spaces)
- Upload any concrete image
- Get instant segmentation results
- Adjust detection threshold
- No installation required!
# Clone the repository
git clone https://github.com/samir-m0hamed/concrete-crack-segmentation.git
cd concrete-crack-segmentation
# Install dependencies
pip install -r requirements.txtimport torch
from PIL import Image
from inference import CrackSegmentationModel
# Initialize model
model = CrackSegmentationModel('unet_model_weights.pth')
# Make prediction on single image
prediction = model.predict('image.jpg', threshold=0.5)
# Access results
segmentation_mask = prediction['mask'] # Binary mask
probability_map = prediction['probability'] # Soft predictions
crack_percentage = prediction['crack_pct'] # Crack coverage %from huggingface_hub import hf_hub_download
import torch
# Download model from HF
model_path = hf_hub_download(
repo_id="samir-mohamed/concrete-crack-segmentation",
filename="unet_model_weights.pth"
)
# Load weights
model = torch.load(model_path)from torch.utils.data import DataLoader
from dataset import CrackDataset
# Load dataset
dataset = CrackDataset(
images_path='path/to/images',
masks_path='path/to/masks'
)
loader = DataLoader(dataset, batch_size=32)
# Process batch
for images, masks in loader:
with torch.no_grad():
predictions = model(images)Input (3, 256, 256)
β
Encoder Block 1: Conv(64) β BatchNorm β ReLU β Dropout β MaxPool
β
Encoder Block 2: Conv(128) β BatchNorm β ReLU β Dropout β MaxPool
β
Encoder Block 3: Conv(256) β BatchNorm β ReLU β Dropout β MaxPool
β
Encoder Block 4: Conv(512) β BatchNorm β ReLU β Dropout β MaxPool
β
Bottleneck: Conv(1024) β Conv(1024)
β
Decoder Block 1: UpConv(512) + Skip Connection β Conv(512)
β
Decoder Block 2: UpConv(256) + Skip Connection β Conv(256)
β
Decoder Block 3: UpConv(128) + Skip Connection β Conv(128)
β
Decoder Block 4: UpConv(64) + Skip Connection β Conv(64)
β
Output Conv (1, 256, 256) β Sigmoid
β
Output (1, 256, 256)
| Component | Specification |
|---|---|
| Framework | PyTorch 2.0+ |
| Input Shape | (3, 256, 256) RGB images |
| Output Shape | (1, 256, 256) binary masks |
| Total Parameters | ~7.8M |
| Model Size | ~30 MB |
| Encoder Filters | [64, 128, 256, 512] |
| Bottleneck Filters | 1024 |
| Activation | ReLU (intermediate), Sigmoid (output) |
| Normalization | BatchNorm2d |
| Regularization | Dropout2d (p=0.2-0.3) |
| Inference Time | ~50-100ms per image (GPU) |
optimizer = AdamW(
lr=2e-3,
weight_decay=1e-5
)
scheduler = CosineAnnealingLR(
T_max=50,
eta_min=1e-6
)
loss = CombinedLoss(
bce_weight=0.4,
dice_weight=0.6
)
training_config = {
'batch_size': 16,
'epochs': 25,
'train_val_split': 0.8,
'early_stopping_patience': 15,
'image_size': (256, 256)
}Applied to training set only:
- Random Flip: Horizontal & Vertical (p=0.5)
- Random Rotation: Β±15 degrees
- Random Affine: Translate Β±10%, Scale 0.8-1.2
- Color Jitter: Brightness, contrast, saturation Β±0.2
- Gaussian Blur: Ο: 0.1-2.0
- Normalization: ImageNet statistics
GPU: NVIDIA CUDA-compatible (T4, A100, etc.)
VRAM: 6GB+
CPU: Intel i7 / AMD Ryzen
RAM: 16GB+
Runtime: ~17 minutes (25 epochs)
| Property | Value |
|---|---|
| Total Images | 800 |
| Image Format | RGB JPG (256Γ256) |
| Mask Format | Binary PNG (256Γ256) |
| Train/Val Split | 80/20 (640/160 images) |
| Mask Values | {0.6549 (cracks), 1.0 (background)} |
| Average Crack Area | 0-3% per image |
| Special Feature | 100% automatic mask inversion detection |
- Automatic Inversion Detection: Detects if masks are black=cracks or white=cracks
- Stratified Split: Maintains balanced distribution in train/val
- Image Normalization: ImageNet mean/std standardization
- Mask Format: Converted to normalized [0, 1] range
- Python 3.9+
- 8GB RAM
- CPU-capable machine
- ~500MB disk space
- Python 3.10+
- 16GB RAM
- NVIDIA GPU with 6GB+ VRAM
- CUDA 11.8+
- ~1GB disk space (with model)
torch==2.0.0
torchvision==0.15.0
Pillow==9.5.0
numpy==1.24.3
opencv-python==4.8.0.74
matplotlib==3.7.2
tqdm==4.66.1
huggingface-hub>=0.17.0
Install all with:
pip install -r requirements.txtconcrete-crack-segmentation/
βββ Concreate_Crack_Segmentation.ipynb # Main training notebook
βββ Dataset/
β βββ images/ # 800 concrete images (256x256)
β βββ masks/ # Corresponding crack masks
βββ unet_model_weights.pth # Trained model weights (118MB)
βββ requirements.txt # Python dependencies
βββ .gitattributes
βββ README.md
Input Image (any size)
β
Resize to 256Γ256
β
Convert to tensor
β
Normalize (ImageNet stats)
β
Model forward pass
β
Apply sigmoid activation
β
Threshold at 0.5 (adjustable)
β
Output: Binary segmentation mask
import torch
from PIL import Image
import torchvision.transforms as transforms
# Load and preprocess
image = Image.open('concrete.jpg').convert('RGB')
image = transforms.Resize((256, 256))(image)
# Normalize with ImageNet stats
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=mean, std=std)
])
# Predict
image_tensor = transform(image).unsqueeze(0)
with torch.no_grad():
logits = model(image_tensor)
prediction = torch.sigmoid(logits)
binary_mask = (prediction > 0.5).float()
# Get statistics
crack_pixels = binary_mask.sum().item()
crack_percentage = (crack_pixels / (256 * 256)) * 100# Lower threshold = more sensitive (catches smaller cracks)
# Higher threshold = less sensitive (only major cracks)
threshold = 0.3 # More sensitive
mask_sensitive = (prediction > threshold).float()
threshold = 0.7 # Less sensitive
mask_strict = (prediction > threshold).float()To retrain on your own dataset:
# Modify in notebook:
BATCH_SIZE = 32 # Increase for more data
NUM_EPOCHS = 50 # More epochs for convergence
LEARNING_RATE = 1e-3 # Adjust learning rate
IMG_SIZE = (512, 512) # Different image size# Load pre-trained weights
model = ImprovedUNet(...)
model.load_state_dict(torch.load('unet_model_weights.pth'))
# Freeze encoder for transfer learning
for param in model.encoder.parameters():
param.requires_grad = False
# Train only decoder
optimizer = torch.optim.Adam(
filter(lambda p: p.requires_grad, model.parameters()),
lr=1e-4
)Solution: Set num_workers=0 in DataLoader
loader = DataLoader(dataset, batch_size=16, num_workers=0)Solution: Reduce batch size
BATCH_SIZE = 8 # Instead of 16
loader = DataLoader(dataset, batch_size=BATCH_SIZE)Solution: Check mask format
# Verify masks are normalized to [0, 1]
print(mask_tensor.min(), mask_tensor.max()) # Should be [0, 1]
# Verify mask format matches dataset
print((mask_tensor == 0).sum() / mask_tensor.numel() * 100, "% background")Solution: Check input normalization
# Verify ImageNet normalization applied
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
transform = transforms.Normalize(mean=mean, std=std)- MODEL_CARD.md - Detailed model information & performance metrics
- Concreate_Crack_Segmentation.ipynb - Complete training notebook with explanations
Existing Space: https://huggingface.co/spaces/samir-mohamed/concrete-crack-segmentation
Epoch 1: Dice: 45.23%, Loss: 0.1298
Epoch 5: Dice: 92.15%, Loss: 0.0456
Epoch 10: Dice: 97.63%, Loss: 0.0234
Epoch 15: Dice: 99.21%, Loss: 0.0168
Epoch 20: Dice: 99.68%, Loss: 0.0152
Epoch 25: Dice: 99.77%, Loss: 0.0139 β Final
Model converges quickly and stabilizes by epoch 7.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
If you use this model in your research, please cite:
@software{concrete_crack_segmentation_2026,
title={Concrete Crack Segmentation with UNet},
author={samir-m0hamed},
year={2026},
url={https://github.com/samir-m0hamed/concrete-crack-segmentation}
}MIT License .
This project is open source and available for educational and commercial use.
- Architecture: Based on UNet by Ronneberger et al. (2015)
- Loss Function: Dice Loss by Milletari et al. (2016)
- Dataset: Concrete Crack Dataset (800 annotated images)
- Framework: PyTorch, TorchVision
- GitHub Issues: Report bugs or request features
- Hugging Face Model: https://huggingface.co/samir-mohamed/concrete-crack-segmentation
- GitHub Repository: https://github.com/samir-m0hamed/concrete-crack-segmentation
Made for infrastructure inspection & structural health monitoring
Last Updated: March 28, 2026 | Status: β Production Done