Image Augmentation for CV Model Training

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1 servicesAll 1566 services
Image Augmentation for CV Model Training
Simple
from 1 business day to 3 business days
FAQ
AI Development Areas
AI Solution Development Stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823
  • image_logo-aider_0.jpg
    AIDER company logo development
    762
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    848

Аугментация изображений для обучения CV-моделей

Датасет из 800 изображений стал датасетом из 24 000 — звучит красиво, но если аугментации неправильные, модель просто переобучится на артефакты самих трансформаций. Ключевой принцип: аугментации должны моделировать реальные вариации в production-данных, а не просто раздувать датасет.

Albumentations — основной инструмент

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:
    """
    Политика аугментации зависит от домена.
    Outdoor — агрессивные изменения освещения и погоды.
    Medical — только геометрические, цветовые противопоказаны.
    """
    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 = [
        # Для медицинских снимков — только contrast/gamma
        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)

MixUp и CutMix для регуляризации

Простые аугментации не убирают переобучение при малом датасете. MixUp и CutMix создают новые примеры смешением двух изображений — это существенная регуляризация:

import torch
import numpy as np

def mixup_batch(
    images: torch.Tensor,     # (B, C, H, W)
    labels: torch.Tensor,     # (B,) для классификации
    alpha: float = 0.4
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, float]:
    """
    MixUp: pixel-level смешение двух изображений.
    Требует soft labels при вычислении лосса.
    """
    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]:
    """
    CutMix: вырезаем регион из одного изображения и вставляем в другое.
    Лучше MixUp для задач с локальными признаками.
    """
    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 с учётом clipping
    lam = 1 - (y2-y1) * (x2-x1) / (H * W)

    return mixed, labels, labels[perm], lam

# Использование в training loop
def mixup_criterion(criterion, pred, y_a, y_b, lam):
    return lam * criterion(pred, y_a) + (1 - lam) * criterion(pred, y_b)

Синтетические данные через Stable Diffusion

Когда реальных данных катастрофически мало (менее 100 примеров класса), синтетика через inpainting или ControlNet даёт прирост AP на 8–15%:

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

На практике: 80 реальных примеров трещин + 400 синтетических подняли recall с 0.61 до 0.78 при сохранении precision > 0.80.

Сравнение стратегий аугментации

Стратегия Прирост accuracy Вычислительная стоимость Применимость
Базовые геометрические +3–7% Минимальная Всегда
Фотометрические +5–12% Минимальная Не для medical
MixUp / CutMix +4–10% Минимальная Классификация
Mosaic (YOLO) +7–15% Низкая Детекция мелких объектов
AutoAugment / RandAugment +5–12% Средняя Общий случай
Синтетика (SD/ControlNet) +8–20% Высокая Дефицит данных

Сроки

Работа Срок
Настройка pipeline аугментации для задачи 1–2 недели
Генерация синтетических данных (500–2000 примеров) 2–4 недели
Полный эксперимент: baseline → аугментации → синтетика 4–6 недель