Computer Vision Engineer: от детекции и Doc-AI до продакшнаДанные и разметкаСбор данных и схема классов

Сбор данных и схема классов

Уроки курсаСбор данных и схема классов

Практика: собрать 300 изображений (или взять демо-пак)

Для обучения детектора нужны данные. Минимум 300 изображений (лучше 1000+), разнообразные — разные углы, освещение, фоны. Три варианта: скачать публичный датасет (COCO subset, Open Images), собрать свои фото, использовать демо-пак из курса. Проверяем качество (не размытые, не слишком мелкие), лицензию (можно ли использовать коммерчески), разнообразие классов.

Ключевые идеи

  • Минимум 300 изображений на класс для приемлемого качества, 1000+ для хорошего.
  • Разнообразие важнее количества: разные ракурсы, условия, фоны.
  • Проверяем лицензию: Creative Commons, Public Domain — можно использовать.
  • Качество: разрешение ≥640px, не размытые, объекты видны чётко.
  • Демо-пак для быстрого старта — 300 размеченных изображений из курса.

Вариант 1: Публичные датасеты

Быстрый способ — скачать subset популярного датасета.

COCO subset:

# Скачать subset COCO (например, только категории person, car, dog)
# Используем fiftyone для фильтрации
pip install fiftyone

python <<EOF
import fiftyone as fo
import fiftyone.zoo as foz

# Загружаем COCO train split
dataset = foz.load_zoo_dataset(
    "coco-2017",
    split="train",
    label_types=["detections"],
    classes=["person", "car", "dog"],  # Только нужные классы
    max_samples=300  # Ограничение количества
)

# Экспортируем в YOLO формат
dataset.export(
    export_dir="data/coco_subset",
    dataset_type=fo.types.YOLOv5Dataset,
    label_field="ground_truth"
)
EOF

Open Images subset:

# Скачать Open Images с помощью OIDv4 toolkit
pip install oidv4-toolkit

# Скачать 300 изображений класса "Car"
oidv4 downloader --classes Car --type_csv train --limit 300

Вариант 2: Собственные фото

Если задача специфична (дефекты продукции, редкие объекты), собираем сами.

Что учитывать:

  • Разнообразие ракурсов: сверху, сбоку, снизу, крупный план, общий план.
  • Условия съёмки: день/ночь, солнце/тень, разные фоны.
  • Качество: не размытые, объект занимает 10-80% кадра (не слишком мелкий/крупный).
  • Камеры: используйте разные устройства (телефон, камера) — модель будет robustнее.

Пример скрипта для сбора с веб-камеры:

import cv2
from pathlib import Path

def capture_images(output_dir, num_images=300):
    """Захватывает изображения с веб-камеры."""
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)
    
    cap = cv2.VideoCapture(0)
    count = 0
    
    print(f"Нажмите SPACE для захвата, ESC для выхода")
    print(f"Цель: {num_images} изображений")
    
    while count < num_images:
        ret, frame = cap.read()
        if not ret:
            break
        
        # Отображаем счётчик
        cv2.putText(frame, f"Captured: {count}/{num_images}", (10, 30),
                    cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
        cv2.imshow('Capture', frame)
        
        key = cv2.waitKey(1)
        if key == 27:  # ESC
            break
        elif key == 32:  # SPACE
            filename = output_dir / f"image_{count:04d}.jpg"
            cv2.imwrite(str(filename), frame)
            print(f"Сохранено: {filename}")
            count += 1
    
    cap.release()
    cv2.destroyAllWindows()
    print(f"Захвачено {count} изображений")

# Использование
capture_images("data/raw/captured", num_images=300)

Вариант 3: Скачивание из интернета

Скачиваем с сайтов с открытыми лицензиями (Unsplash, Pexels).

Пример скрипта:

import requests
from pathlib import Path
import time

def download_images(urls, output_dir):
    """Скачивает изображения по списку URL."""
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)
    
    successful = 0
    failed = 0
    
    for i, url in enumerate(urls):
        try:
            response = requests.get(url, timeout=10)
            response.raise_for_status()
            
            # Определяем расширение из Content-Type или URL
            ext = url.split('.')[-1].split('?')[0]
            if ext not in ['jpg', 'jpeg', 'png']:
                ext = 'jpg'
            
            filename = output_dir / f"image_{i:04d}.{ext}"
            filename.write_bytes(response.content)
            
            print(f"✓ Скачано: {filename.name}")
            successful += 1
            time.sleep(0.5)  # Чтобы не заблокировали
            
        except Exception as e:
            print(f"✗ Ошибка {url}: {e}")
            failed += 1
    
    print(f"\nГотово: {successful} успешно, {failed} ошибок")

# Пример: список URL (замените на реальные)
urls = [
    "https://images.unsplash.com/photo-1?w=640",
    "https://images.pexels.com/photo-2?w=640",
    # ... добавить 300 URL
]

download_images(urls, "data/raw/downloaded")

Важно: проверьте лицензию каждого сайта. Unsplash License, Pexels License — бесплатно, можно использовать коммерчески.

Демо-пак: если не хотите сразу собирать свои данные, скачайте учебный датасет на 300 размеченных изображений кошек и собак в YOLO-формате:

Скачать cv-demo-pet-300-yolo.zip

Проверка качества изображений

Скрипт для проверки разрешения и валидности:

from PIL import Image
from pathlib import Path

def check_images(images_dir, min_resolution=640):
    """Проверяет качество изображений."""
    images_dir = Path(images_dir)
    
    valid = 0
    invalid = []
    
    for img_path in images_dir.glob('*.jpg'):
        try:
            img = Image.open(img_path)
            w, h = img.size
            
            # Проверка минимального разрешения
            if min(w, h) < min_resolution:
                invalid.append(f"{img_path.name}: разрешение {w}x{h} < {min_resolution}px")
            else:
                valid += 1
                
        except Exception as e:
            invalid.append(f"{img_path.name}: ошибка открытия - {e}")
    
    print(f"Проверено: {valid + len(invalid)} изображений")
    print(f"Валидных: {valid}")
    print(f"Невалидных: {len(invalid)}")
    
    if invalid:
        print("\nПроблемные файлы:")
        for issue in invalid[:10]:  # Показываем первые 10
            print(f"  {issue}")

check_images("data/raw/downloaded", min_resolution=640)

Частые проблемы

  1. Мало разнообразия: все фото с одного ракурса — модель переобучится. Снимайте с разных углов, расстояний.
  2. Низкое качество: размытые, тёмные, мелкие объекты. Модель не научится их детектировать. Фильтруйте плохие изображения.
  3. Нарушение лицензии: скачали с Google Images без проверки — могут быть авторские права. Используйте только открытые источники.
  4. Неправильные форматы: PNG вместо JPG, HEIC с iPhone. Конвертируйте в JPG/PNG для совместимости.
  5. Дубликаты: одно изображение попало в train и val — завышенные метрики. Проверьте уникальность (по хешу).

Проверка на дубликаты

import hashlib
from pathlib import Path

def find_duplicates(images_dir):
    """Находит дубликаты по хешу."""
    hashes = {}
    duplicates = []
    
    for img_path in Path(images_dir).rglob('*.jpg'):
        with open(img_path, 'rb') as f:
            file_hash = hashlib.md5(f.read()).hexdigest()
        
        if file_hash in hashes:
            duplicates.append((img_path, hashes[file_hash]))
        else:
            hashes[file_hash] = img_path
    
    if duplicates:
        print(f"Найдено {len(duplicates)} дубликатов:")
        for dup, original in duplicates[:5]:
            print(f"  {dup.name} = {original.name}")
    else:
        print("Дубликатов не найдено")

find_duplicates("data/images/")

Связанные понятия

  • Web scraping: автоматический сбор изображений с сайтов (BeautifulSoup, Scrapy).
  • Synthetic data: генерация синтетических изображений (Blender, Unity) для аугментации.
  • Active learning: модель сама выбирает, какие изображения нужны для улучшения.

Итоги

  • Минимум 300 изображений на класс, лучше 1000+ для хорошего качества.
  • Варианты: публичные датасеты (быстро), собственные фото (специфичные задачи).
  • Проверяем качество: разрешение ≥640px, не размытые, разнообразные.
  • Лицензия: Unsplash, Pexels, Creative Commons — безопасно для коммерции.
  • Валидация: проверяем разрешение, форматы, дубликаты перед разметкой.

Дальше по курсу

В следующем шаге создадим data.yaml для собранных изображений, проверим пути (все ли файлы на месте), разобьём на train/val/test с помощью скрипта. Подготовим датасет к разметке в CVAT или LabelImg.