Компьютерное зрение с нуля: OpenCV и YOLO на практике

Практический курс по созданию систем обнаружения объектов с использованием OpenCV и YOLO. Минимум теории, максимум рабочего кода: от настройки среды до готового детектора с подсчётом объектов на видео.

1. Настройка среды разработки и первые шаги с OpenCV

Настройка среды разработки и первые шаги с OpenCV

Представьте: вы смотрите на фотографию и мгновенно видите на ней кофе, ноутбук и розетку. Компьютер видит лишь массив чисел — 1920×1080 пикселей, каждый из которых закодирован тремя числами от 0 до 255. Задача компьютерного зрения — научить машину извлекать из этого хаоса осмысленную информацию. И первый шаг к этому — правильно настроенная среда и понимание того, как OpenCV работает с изображениями на базовом уровне.

Установка Python и создание виртуального окружения

Прежде чем устанавливать библиотеки, создадим изолированное окружение. Это защитит проект от конфликтов версий — ситуация, когда один пакет требует numpy 1.24, а другой — numpy 1.26, встречается постоянно.

После активации в терминале появится префикс (cv_env) — значит, все пакеты будут установлены именно в это окружение.

Установка OpenCV

OpenCV существует в двух пакетах: opencv-python (основные модули) и opencv-contrib-python (дополнительные алгоритмы — SIFT, SURF, ArUco). Для нашего курса достаточно базовой версии.

> Если при импорте появляется ошибка ImportError: libGL.so.1, установите системную библиотеку: apt-get install libgl1-mesa-glx (Linux) или используйте headless-версию: pip install opencv-python-headless.

Проверим установку:

Чтение и отображение изображений

OpenCV загружает изображения как объекты numpy.ndarray — трёхмерный массив, где первые два измерения — высота и ширина, третье — цветовые каналы. Важный нюанс: OpenCV использует порядок каналов BGR (Blue-Green-Red), а не привычный RGB.

Функция cv2.imread() принимает второй аргумент — флаг загрузки:

| Флаг | Значение | Результат | |------|----------|-----------| | cv2.IMREAD_COLOR | 1 | Цветное изображение (BGR, 3 канала) | | cv2.IMREAD_GRAYSCALE | 0 | Оттенки серого (1 канал) | | cv2.IMREAD_UNCHANGED | -1 | Как есть (включая альфа-канал) |

Базовые операции с пикселями

Каждый пиксель — это элемент массива numpy. Можно обращаться к нему напрямую:

Представьте, что изображение — это лист клетчатой бумаги. Координата img[y, x] — это строка y и столбец x. Область img[50:200, 100:300] — прямоугольник от строки 50 до 200 и от столбца 100 до 300.

Конвертация цветовых пространств

Для компьютерного зрения оттенки серого — не просто «чёрно-белая картинка». Это уменьшение размерности данных в 3 раза при сохранении структурной информации (контуры, текстуры, градиенты яркости).

Пространство HSV особенно полезно для сегментации по цвету: компонента H (оттенок) отделена от яркости, поэтому объекты одного цвета хорошо определяются даже при разном освещении.

Сохранение изображений

Работа с matplotlib для визуализации

cv2.imshow() открывает отдельное окно, что неудобно в Jupyter Notebook. Matplotlib позволяет отображать изображения прямо в ячейке:

Если забыть конвертировать BGR в RGB, цвета будут инвертированы — красное небо и синяя трава. Это одна из самых частых ошибок начинающих.

Практический пример: пакетная обработка

Допустим, нужно привести все фотографии в папке к размеру 640×480 и сохранить в оттенках серого:

Этот простой скрипт — прототип пайплайна предобработки данных, который в реальных проектах компьютерного зрения обрабатывает тысячи изображений перед обучением нейросети.

На этом этапе у вас есть рабочая среда и понимание того, как OpenCV представляет изображения. В следующей статье мы перейдём к работе с видеопотоком и более сложным операциям обработки — фильтрации, выделению контуров и морфологическим преобразованиям.

2. Работа с видеопотоком и базовые операции с изображениями

Работа с видеопотоком и базовые операции с изображениями

Видеокамера на парковке супермаркета генерирует 25 кадров в секунду. Каждый кадр — это обычное изображение, которое можно обработать теми же инструментами, что и фотографию. Но когда кадров тысячи, появляются новые задачи: как эффективно читать поток, как не потерять производительность и как извлечь из последовательности кадров полезную информацию. Именно этому посвящена эта статья.

Чтение видео из файла

Видеофайл — это последовательность сжатых кадров. OpenCV распаковывает их по одному через объект VideoCapture:

Метод cap.read() возвращает кортеж (success, frame). Когда кадры заканчиваются, success становится False. Функция cv2.waitKey(1) не только ждёт 1 мс, но и обрабатывает события окна — без неё окно зависнет.

Захват с веб-камеры

Единственное отличие — вместо пути к файлу передаём индекс камеры:

На практике веб-камеры часто выдают 30 FPS, а разрешение зависит от модели. Чтобы принудительно задать параметры:

> Не все камеры поддерживают произвольные настройки. Если после set() значения не изменились — камера просто игнорирует запрос. Проверяйте через cap.get().

Запись видео

Для сохранения обработанных кадров используется VideoWriter:

> VideoWriter требует, чтобы количество каналов входного кадра совпадало с ожидаемым. Если пишете grayscale — конвертируйте обратно в BGR или создавайте writer с параметром isColor=False.

Размытие и фильтрация

Шум на изображении — это случайные колебания яркости отдельных пикселей. Для его подавления применяют свёрточные фильтры: матрица (ядро) скользит по изображению, заменяя каждый пиксель взвешенным средним соседей.

| Фильтр | Когда использовать | Скорость | |--------|-------------------|----------| | blur | Быстрое подавление шума | Быстро | | GaussianBlur | Общее сглаживание | Средне | | medianBlur | Импульсный шум («соль-перец») | Медленнее | | bilateralFilter | Нужно сохранить границы | Медленно |

Параметр sigmaX в GaussianBlur контролирует «разброс» Гауссианы: чем больше — тем сильнее размытие. Если передать 0 — значение вычисляется автоматически из размера ядра.

Выделение контуров

Контуры — это границы объектов на изображении. OpenCV находит их на бинарном (чёрно-белом) изображении, где белые области — это объекты, а чёрные — фон.

Метод Кэнни работает в несколько этапов: сначала вычисляет градиент яркости (производные по x и y), затем подавляет немаксимумы и применяет двойной порог. Пороги threshold1 и threshold2 определяют, какие границы считать слабыми, а какие — сильными.

Для каждого найденного контура можно извлечь геометрические характеристики:

Морфологические операции

После бинаризации контуры часто бывают «рваными» — с дырами и лишними отростками. Морфологические операции исправляют это:

Представьте, что вы фотографируете текст на белой бумаге. После бинаризации буквы «р» и «а» могут иметь разрывы в тонких элементах. Closing заполнит эти разрывы. А мелкие точки шума вокруг букв уберёт Opening.

Пороговая бинаризация

Для превращения градаций серого в чёткие чёрно-белые границы:

Адаптивная бинаризация незаменима, когда освещение неравномерно: левая часть кадра яркая, правая — тёмная. Глобальный порог 127 не справится, а адаптивный вычислит порог отдельно для каждого фрагмента.

Практический сценарий: обработка видеопотока в реальном времени

Этот пайплайн — основа для систем детекции движения, подсчёта объектов и распознавания жестов. Каждый кадр проходит через конвейер: захват → конвертация цвета → сглаживание → выделение границ. В следующей статье мы разберём, как нейросеть YOLO заменяет ручное выделение контуров автоматическим обнаружением объектов.

3. Архитектура YOLO: как работает детектор объектов

Архитектура YOLO: как работает детектор объектов

Классический подход к поиску объектов на изображении напоминает поиск иголки в стоге сена: скользящее окно пробегает по всей картинке, проверяя каждый фрагмент — «здесь кошка? а здесь? а тут?». На одно изображение уходили десятки секунд. В 2015 году Джозеф Редмон предложил революционную идею: а что если прогнать изображение через нейросеть один раз и сразу получить все объекты с их координатами? Так появился YOLOYou Only Look Once.

Проблема: почему классические методы медленные

До YOLO доминировали двухстадийные детекторы (R-CNN, Fast R-CNN, Faster R-CNN). Их логика:

  • Генерация регионов-кандидатов — алгоритм предлагает сотни или тысячи прямоугольных областей, где может находиться объект.
  • Классификация каждого региона — каждая область прогоняется через классификатор: «это кошка, собака или фон?».
  • Точность высокая, но скорость — 5–7 кадров в секунду. Для видеонаблюдения или беспилотного автомобиля этого недостаточно.

    Решение YOLO: одна прогонка — все объекты

    YOLO рассматривает детекцию как задачу регрессии. Вместо двух стадий сеть делает один проход (forward pass) через всю картинку и за один раз предсказывает:

  • Где находятся ограничивающие рамки (bounding boxes)
  • Какой класс объекта в каждой рамке
  • Насколько сеть уверена в своём предсказании
  • Представьте, что вы бросаете быстрый взгляд на фотографию. За долю секунды вы замечаете: «Вот кошка на диване, вот человек у окна, вот ваза на столе». Вы не проверяете каждый сантиметр изображения — мозг обрабатывает всё целиком. YOLO делает нечто подобное.

    Как изображение превращается в предсказания

    Архитектура YOLO разбивает входное изображение на сетку ячеек. Каждая ячейка отвечает за объекты, центр которых попал в её область.

    Для каждой ячейки сеть предсказывает:

  • ограничивающих рамок, каждая из которых содержит 5 параметров: , , , и уверенность (confidence)
  • Вероятности принадлежности к классам
  • Здесь и — координаты центра рамки относительно ячейки, и — ширина и высота относительно всего изображения. Уверенность вычисляется как:

    где — вероятность наличия объекта в рамке, а IoU (Intersection over Union) — отношение площади пересечения предсказанной и истинной рамок к площади их объединения. IoU = 1 означает идеальное совпадение, IoU = 0 — рамки не пересекаются.

    Итоговый тензор предсказаний имеет размерность .

    Свёрточная архитектура

    YOLO использует свёрточную нейронную сеть (CNN) для извлечения признаков. Классическая архитектура YOLOv1:

  • 24 свёрточных слоя извлекают визуальные признаки (грани, текстуры, формы)
  • 2 полносвязных слоя преобразуют признаки в вектор предсказаний
  • Свёрточные слои работают как иерархия: первые слои распознают простые паттерны (линии, углы), средние — комбинации (колёса, глаза), глубокие — целые объекты (автомобиль, лицо).

    В последующих версиях (v2, v3, v4, v5 и далее) архитектура усложнялась:

    | Версия | Ключевое улучшение | Год | |--------|-------------------|-----| | YOLOv1 | Базовая концепция «одного прохода» | 2015 | | YOLOv2 (YOLO9000) | Batch Normalization, якоря (anchors), много масштабов | 2016 | | YOLOv3 | FPN-подобные связи, 3 масштаба предсказаний | 2018 | | YOLOv4 | CSPDarknet, Mish-активация, аугментации | 2020 | | YOLOv5 | PyTorch-реализация, автоматические якоря, экспорт | 2020 | | YOLOv8+ | Anchor-free, улучшенные головы, трекинг | 2023 |

    Механизм якорей (Anchors)

    В YOLOv2–v7 используется концепция якорных рамок (anchor boxes) — заранее заданных шаблонов размеров и пропорций. Сеть не предсказывает абсолютные размеры рамки с нуля, а корректирует существующий шаблон.

    Например, для детекции людей подходят узкие высокие якоря, для машин — широкие и низкие. Якоря определяются через k-means кластеризацию на обучающем датасете: находятся наиболее типичных соотношений сторон.

    > В YOLOv8 и новее используется anchor-free подход: сеть предсказывает расстояния от центра объекта до каждой стороны рамки напрямую, без привязки к шаблонам. Это упрощает настройку и улучшает работу с объектами нестандартных пропорций.

    Non-Maximum Suppression (NMS)

    Одна проблема: сеть может предсказать несколько рамок для одного объекта. NMS — алгоритм, который оставляет только лучшую:

  • Отсортировать все рамки по убыванию confidence.
  • Взять рамку с наибольшим confidence — она «победитель».
  • Удалить все рамки, у которых IoU с победителем выше порога (обычно 0.5).
  • Повторить для оставшихся рамок.
  • Представьте очередь из кандидатов на одну должность. Берём самого квалифицированного, а остальных, кто слишком похож на него (высокое пересечение), отсеиваем. Так для каждого объекта остаётся ровно одна рамка.

    Метрики качества: mAP

    Для оценки детекторов используется mAP (mean Average Precision) — средняя точность по всем классам при пороге IoU (обычно 0.5, отсюда mAP@0.5).

  • Precision — доля правильных предсказаний среди всех предсказаний.
  • Recall — доля найденных объектов среди всех реальных объектов.
  • AP — площадь под кривой Precision-Recall для одного класса.
  • mAP — среднее AP по всем классам.
  • Чем ближе mAP к 1.0, тем лучше модель. Современные версии YOLO достигают mAP@0.5 выше 0.55 на датасете COCO (80 классов, более 330 000 изображений).

    Почему YOLO — стандарт для реального времени

    Ключевое преимущество YOLO — скорость. Однопроходная архитектура позволяет обрабатывать 30–150+ кадров в секунду на GPU, в зависимости от версии и размера входа. Для сравнения, Faster R-CNN работает на уровне 5–15 FPS.

    | Модель | mAP@0.5 (COCO) | Скорость (FPS, GPU) | |--------|----------------|---------------------| | YOLOv8n | 0.37 | ~800 | | YOLOv8s | 0.45 | ~400 | | YOLOv8m | 0.50 | ~200 | | YOLOv8l | 0.53 | ~120 | | YOLOv8x | 0.54 | ~80 |

    Буквы n/s/m/l/x обозначают размер модели: nano — самая лёгкая и быстрая, xlarge — самая тяжёлая и точная. Выбор зависит от задачи: для встраиваемых устройств подойдёт nano, для серверной обработки — large или xlarge.

    Теперь, когда вы понимаете, как YOLO устроен внутри, в следующей статье мы перейдём к практике: загрузим предобученную модель и запустим детекцию объектов в реальном времени.

    4. Детекция объектов в реальном времени с помощью YOLO

    Детекция объектов в реальном времени с помощью YOLO

    Видеокамера на складе фиксирует каждую коробку, проходящую через зону контроля. Система определяет класс товара, рисует рамку и отправляет данные в учётную систему — всё это за 30 миллисекунд на кадр. Такие системы работают сегодня на заводах, в магазинах и на дорогах. В этой статье мы построим подобный прототип: загрузим предобученную модель YOLO и подключим её к видеопотоку.

    Установка библиотеки Ultralytics

    Ultralytics — компания, поддерживающая современные версии YOLO (начиная с v5). Их Python-пакет предоставляет единый API для всех версий:

    При первом запуске модель автоматически скачается с сервера (~6 МБ для nano-версии). Кэширование происходит в папку ~/.cache/ultralytics/.

    Загрузка модели и первый запуск на изображении

    Метод model() принимает не только путь к файлу, но и URL, numpy-массив, список изображений или PIL Image. Это делает интеграцию гибкой.

    Структура результата

    Объект Results содержит всю информацию о предсказаниях:

    Детекция на изображении с визуализацией

    Метод plot() автоматически рисует рамки, подписи классов и уверенность. Цвета назначаются по индексу класса.

    Детекция в реальном времени с веб-камеры

    Параметр verbose=False отключает вывод информации о каждом кадре в консоль — без него терминал будет забит строками вида 0: 480x640 2 persons, 1 car, 12ms.

    Фильтрация по классам и уверенности

    Не всегда нужно детектировать все 80 классов. Параметры classes и conf позволяют сузить результаты:

    Полный список классов COCO:

    Работа с координатами рамок

    Для дальнейшей обработки — подсчёта, трекинга, анализа пересечений — нужны координаты в удобном формате:

    Ручная отрисовка полезна, когда нужен нестандартный вид: например, рамки разного цвета для разных классов или дополнительная информация (номер трека, направление движения).

    Выбор модели под задачу

    Ultralytics предоставляет несколько размеров моделей. Чем больше модель — тем выше точность, но ниже скорость:

    Для прототипирования и работы на ноутбуке без дискретной видеокарты — nano. Для продакшена на сервере — medium или large. Разница в точности между nano и large на COCO составляет около 17 процентных пунктов mAP@0.5.

    Обработка видеофайла с сохранением результата

    Типичные ошибки и их решения

    Ошибка RuntimeError: CUDA out of memory. — модель не помещается в видеопамять GPU. Решение: использовать меньшую модель (yolov8n.pt) или уменьшить размер входа через параметр imgsz:

    Медленная обработка на CPU. YOLO на процессоре работает в 10–50 раз медленнее, чем на GPU. Для ускорения без GPU можно экспортировать модель в формат ONNX:

    Неверные цвета при отображении через matplotlib. Помните: result.plot()` возвращает BGR. Для matplotlib конвертируйте:

    На этом этапе у вас есть рабочая система детекции объектов в реальном времени. В следующей статье мы пойдём дальше: добавим трекинг объектов и построим полноценную систему подсчёта — например, для подсчёта машин, пересекающих линию на видео.

    5. Финальный проект: система подсчёта объектов на видео

    Финальный проект: система подсчёта объектов на видео

    Камера на въезде в парковку считает каждую машину, пересекающую воображаемую линию. Менеджер видит на экране: «Въехало: 47, Выехало: 32, На парковке: 15». Это не сложная инженерная система — это ~100 строк кода на Python с YOLO и OpenCV. В этой статье мы построим такую систему с нуля: от захвата видеопотока до подсчёта объектов с разделением на вход и выход.

    Архитектура системы

    Наша система работает в три этапа на каждом кадре:

  • Детекция — YOLO находит все объекты и возвращает рамки с классами.
  • Трекинг — алгоритм связывает объекты между кадрами, присваивая каждому уникальный ID.
  • Подсчёт — при пересечении объектом заданной линии счётчик увеличивается.
  • Ultralytics встроила трекинг прямо в API модели — достаточно передать параметр tracker:

    Параметр persist=True сохраняет историю треков между кадрами. Трекер botsort.yaml — один из двух доступных (второй — bytetrack.yaml).

    Шаг 1: Базовый захват и детекция с трекингом

    На этом этапе каждый объект получает ID, который сохраняется между кадрами. Рамки подписываются в формате person 1, car 2 и так далее.

    Шаг 2: Извлечение данных трекинга

    Для подсчёта нам нужны координаты центра каждого объекта и его ID:

    Центр рамки — ключевая точка для определения пересечения линии. Если центр объекта переместился с одной стороны линии на другую — объект её пересёк.

    Шаг 3: Определение пересечения линии

    Линия задаётся двумя точками. Для определения, с какой стороны линии находится точка, используем векторное произведение:

    Если знак side_of_line изменился между кадрами — объект пересёк линию. Направление пересечения определяется знаком изменения: положительное → отрицательное означает одно направление, обратное — другое.

    Шаг 4: Полный код системы подсчёта

    Шаг 5: Подсчёт через прямоугольную зону

    Линия — не единственный вариант. Иногда нужно считать объекты внутри зоны (например, количество людей в кадре). Ultralytics предоставляет встроенный инструмент ObjectCounter, как описано в документации Ultralytics:

    Класс ObjectCounter инкапсулирует детекцию, трекинг и подсчёт. Параметр region принимает список точек: две точки — линия, три и более — многоугольник.

    Оптимизация производительности

    При обработке длинных видео или нескольких камер одновременно производительность становится критичной. Несколько приёмов:

    Пропуск кадров. Не обязательно обрабатывать каждый кадр — достаточно каждого второго или третьего:

    Уменьшение разрешения. YOLO по умолчанию масштабирует вход до 640×640. Если исходное видео 1920×1080, можно уменьшить его заранее:

    > При уменьшении разрешения координаты рамок будут в масштабированном пространстве. Для корректной отрисовки на оригинале нужно пересчитать координаты: x1_orig = x1 * (w_orig / w_small).

    Экспорт в ONNX или TensorRT. Для максимальной скорости на GPU:

    TensorRT-модели работают в 2–4 раза быстрее оригинальных PyTorch-весов.

    Расширение системы

    Базовую систему можно развивать в нескольких направлениях:

  • Логирование в CSV/базу данных — записывать время, ID, класс и направление каждого пересечения.
  • Визуализация графиков — строить график потока объектов по времени с помощью matplotlib.
  • Несколько зон — определять несколько линий или полигонов для разных зон подсчёта.
  • Определение скорости — зная расстояние между линиями и FPS, можно оценить скорость движения объектов.
  • Уведомления — при превышении порога (например, больше 100 объектов в час) отправлять алерт.
  • Каждое из этих расширений строится на фундаменте, который вы уже создали: детекция → трекинг → анализ пересечений. Меняется только финальный этап обработки данных.

    Вы прошли путь от установки Python до работающей системы подсчёта объектов. OpenCV даёт полный контроль над обработкой изображений и видео, а YOLO превращает сложную задачу детекции в вызов одной функции. Вместе эти инструменты позволяют создавать системы компьютерного зрения, которые ещё несколько лет назад требовали команды инженеров и месяцев разработки.