Developing an Image Augmentation Pipeline for Training a CV Model

Developing an Image Augmentation Pipeline for Training a CV Model

AI Development Areas

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1285
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1241
  • image_logo-advance_0.webp
    B2B Advance company logo design
    696
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_logo-aider_0.webp
    AIDER company logo development
    919
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1033

Developing an Image Augmentation Pipeline for Training a CV Model

We encountered a typical task: a dataset of 800 images became a dataset of 24,000 — sounds nice, but if augmentations are wrong, the model simply overfits to the artifacts of the transformations themselves. Augmentations should mimic real production data changes, not just increase the number of pictures. Our practice confirms: a properly configured pipeline boosts accuracy by 5–12% without new labeling and saves budget. For example, in a metal defect detection project we initially applied photometric augmentations, but the model started confusing scratches with glare. After switching to geometric and adding synthetic via Stable Diffusion, recall improved from 0.61 to 0.78 with precision above 0.80. Labeling savings ranged from $2,000 to $10,000. On average, clients save from $5,000 to $15,000 per project.

How to choose an augmentation policy for a specific domain?

Configuring augmentations depends on the domain and task. For outdoor, strong illumination and weather changes are needed. For medical, only geometric and cautious photometric. Below is a basic policy on Albumentations that we adapt for each project.

import albumentations as A from albumentations.pytorch import ToTensorV2 def build_augmentation_pipeline( task: str = 'detection', # 'classification' | 'detection' | 'segmentation' domain: str = 'outdoor' # 'outdoor' | 'indoor' | 'medical' | 'satellite' ) -> A.Compose: """ Augmentation policy depends on domain. Outdoor — aggressive illumination and weather changes. Medical — only geometric, color changes contraindicated. """ geometric = [ A.HorizontalFlip(p=0.5), A.ShiftScaleRotate( shift_limit=0.05 if task == 'detection' else 0.1, scale_limit=0.1, rotate_limit=15, border_mode=0, # constant padding p=0.5 ), ] photometric_outdoor = [ A.RandomBrightnessContrast( brightness_limit=0.3, contrast_limit=0.3, p=0.6 ), A.HueSaturationValue( hue_shift_limit=15, sat_shift_limit=40, val_shift_limit=30, p=0.4 ), A.RandomFog(fog_coef_lower=0.1, fog_coef_upper=0.35, p=0.15), A.RandomRain( slant_lower=-10, slant_upper=10, drop_length=15, drop_width=1, drop_color=(200, 200, 200), blur_value=2, brightness_coefficient=0.85, p=0.1 ), A.RandomSunFlare( flare_roi=(0, 0, 1, 0.5), angle_lower=0, num_flare_circles_lower=3, num_flare_circles_upper=6, src_radius=200, p=0.08 ), ] photometric_medical = [ A.RandomGamma(gamma_limit=(80, 120), p=0.4), A.CLAHE(clip_limit=3.0, tile_grid_size=(8, 8), p=0.3), ] photometric = ( photometric_outdoor if domain == 'outdoor' else photometric_medical ) noise = [ A.GaussNoise(var_limit=(10, 50), p=0.3), A.ImageCompression(quality_lower=75, quality_upper=100, p=0.2), A.Blur(blur_limit=3, p=0.1), ] bbox_params = ( A.BboxParams( format='yolo', label_fields=['class_labels'], min_visibility=0.3 ) if task == 'detection' else None ) transforms = geometric + photometric + noise + [ A.Normalize( mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225) ), ToTensorV2() ] return A.Compose(transforms, bbox_params=bbox_params) 

When to use MixUp and CutMix?

Simple augmentations do not eliminate overfitting on small datasets. MixUp and CutMix create new examples by mixing two images — this is good regularization. MixUp, proposed in the paper Zhang et al., 2018 MixUp: Beyond Empirical Risk Minimization, is better for tasks with global features, while CutMix is for local patterns. It is important to control the lambda coefficient: for alpha=0.4, the mixing is moderate, and the model generalizes better.

import torch import numpy as np def mixup_batch( images: torch.Tensor, # (B, C, H, W) labels: torch.Tensor, # (B,) for classification alpha: float = 0.4 ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, float]: lam = np.random.beta(alpha, alpha) batch_size = images.size(0) perm = torch.randperm(batch_size) mixed_images = lam * images + (1 - lam) * images[perm] labels_a = labels labels_b = labels[perm] return mixed_images, labels_a, labels_b, lam def cutmix_batch( images: torch.Tensor, labels: torch.Tensor, alpha: float = 1.0 ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, float]: lam = np.random.beta(alpha, alpha) batch_size, _, H, W = images.shape perm = torch.randperm(batch_size) cut_ratio = np.sqrt(1 - lam) cut_h = int(H * cut_ratio) cut_w = int(W * cut_ratio) cx = np.random.randint(W) cy = np.random.randint(H) x1 = max(cx - cut_w // 2, 0) y1 = max(cy - cut_h // 2, 0) x2 = min(cx + cut_w // 2, W) y2 = min(cy + cut_h // 2, H) mixed = images.clone() mixed[:, :, y1:y2, x1:x2] = images[perm, :, y1:y2, x1:x2] lam = 1 - (y2-y1) * (x2-x1) / (H * W) return mixed, labels, labels[perm], lam def mixup_criterion(criterion, pred, y_a, y_b, lam): return lam * criterion(pred, y_a) + (1 - lam) * criterion(pred, y_b) 

Synthetic data with Stable Diffusion

Note: when real data has fewer than 100 examples per class, synthetic via inpainting or ControlNet gives an AP gain of 8–15%. We use Stable Diffusion Inpainting to generate defects on empty backgrounds. We also use ControlNet for contour generation if precise object insertion is needed.

from diffusers import StableDiffusionInpaintPipeline import torch from PIL import Image pipe = StableDiffusionInpaintPipeline.from_pretrained( 'runwayml/stable-diffusion-inpainting', torch_dtype=torch.float16 ).to('cuda') def generate_synthetic_defect( background_image: Image.Image, mask_image: Image.Image, defect_type: str = 'surface crack' ) -> Image.Image: prompt = ( f'industrial {defect_type}, high resolution, ' f'realistic texture, macro photography' ) result = pipe( prompt=prompt, image=background_image, mask_image=mask_image, num_inference_steps=30, guidance_scale=7.5, strength=0.85 ).images[0] return result 

In practice: 80 real crack examples + 400 synthetic ones raised recall from 0.61 to 0.78 while maintaining precision above 0.80. Labeling savings are significant, often reaching $10,000.

Comparison of augmentation strategies

Strategy Accuracy gain Computational cost Applicability
Basic geometric +3–7% Minimal Always
Photometric +5–12% Minimal Not for medical
MixUp / CutMix +4–10% Minimal Classification
Mosaic (YOLO) +7–15% Low Small object detection
AutoAugment / RandAugment +5–12% Medium General case
Synthetic (SD/ControlNet) +8–20% High Data scarcity

Typical mistakes when configuring augmentations

  • Applying photometric augmentations to medical images — can destroy diagnostic features.
  • Using overly aggressive geometric transformations for detection — bboxes may fall outside the image.
  • Lack of validation set checks — augmentations can change data distribution.
  • Skipping normalization after augmentations — breaks pretrained weights.
  • Incorrect normalization after augmentations — a common cause of metric drops on validation.

What is included in our augmentation pipeline?

We provide:

  • Dataset and domain analysis, identification of key variations.
  • Strategy selection: geometry, photometry, MixUp/CutMix, synthetic data.
  • Pipeline implementation on Albumentations, considering task (bbox, masks).
  • Integration into training loop (PyTorch/TensorFlow).
  • Experiments: A/B testing combinations, metric logging in Weights & Biases.
  • Synthetic data generation (optional) via Stable Diffusion.
  • Model deployment with final pipeline (inference-only transforms).
  • Documentation and team training.

Process overview

  1. Dataset and domain analysis: study data variability, typical production distortions.
  2. Strategy selection: geometry, photometry, MixUp/CutMix, or synthetic.
  3. Pipeline configuration on Albumentations considering task (bbox, masks).
  4. Integration into training loop (PyTorch/TensorFlow).
  5. Experiments: test different combinations, log metrics.
  6. Synthetic data generation (optional) via Stable Diffusion.
  7. Model deployment with final pipeline (inference-only transforms).

Timeline and pricing

Phase Duration
Setting up augmentation pipeline for a specific task 1–2 weeks
Generating synthetic data (500–2000 examples) 2–4 weeks
Full cycle: baseline → augmentations → synthetic → final model 4–6 weeks

Pricing is calculated individually. We guarantee a transparent process and metric tracking at each stage. Contact us to evaluate your project — we'll select the optimal end-to-end augmentation strategy. Order an audit of your current pipeline — get specific recommendations for improvement. Get a consultation today.