Компьютерное зрение — одна из самых динамично развивающихся областей искусственного интеллекта. В нашем мире, где визуальная информация стала доминирующим способом коммуникации, способность машин "видеть" и интерпретировать изображения открывает потрясающие возможности. Обнаружение объектов как подраздел компьютерного зрения занимает особое место, позволяя системам не просто распознавать содержимое изображений, но и точно локализовать конкретные объекты в кадре.
В отличие от классификации изображений, где задача ограничивается определением доминирующего объекта на снимке, обнаружение объектов решает более сложную задачу — находит множественные объекты, определяет их класс и точные координаты каждого. Эта технология сегодня стала неотъемлемой частью беспилотных автомобилей, систем видеонаблюдения, медицинской диагностики и даже обычных смартфонов.
Введение в компьютерное зрение и технологии обнаружения объектов
Эволюция алгоритмов обнаружения объектов прошла долгий путь. Ранние системы, такие как каскады Хаара (Viola-Jones, 2001), позволяли обнаруживать лица с приемлемой точностью, но страдали от множества ограничений. Затем появились подходы на основе гистограмм ориентированных градиентов (HOG) в сочетании с SVM-классификаторами, которые показали хорошие результаты для обнаружения пешеходов, но всё ещё имели ограниченую гибкость. Настоящий прорыв произошел с появлением глубоких сверточных нейронных сетей (CNN). Двухэтапные детекторы, такие как R-CNN, Fast R-CNN и Faster R-CNN, впервые продемонстрировали возможность обнаружения множества классов объектов с высокой точностью. Но им требовалось значительное время обработки, что делало их неприменимыми для задач реального времени.
Тут и появился YOLO (You Only Look Once) – революционный одноэтапный детектор, представленный Джозефом Редмоном в 2015 году. Основная идея YOLO заключается в том, что вся задача детекции сводится к единой регрессионной проблеме. Изображение делится на сетку, и для каждой ячейки сети предсказываются ограничивающие рамки с вероятностями классов – всё это за один проход через нейронную сеть.
Математическая основа современных детекторов объектов лежит в архитектуре CNN, которые обладают способностью извлекать иерархические признаки из изображений. Эти сети состоят из чередующихся сверточных и пулинговых слоёв, за которыми следуют полносвязные слои. Сверточные слои выполняют операцию свертки входного тензора с набором фильтров, что позволяет выделять такие особенности как края, текстуры, а на более высоких уровнях – части объектов и целые объекты. Более новые архитектуры, такие как трансформеры, изначально разработанные для обработки естественного языка, теперь находят применение и в компьютерном зрении. Модели типа DETR (DEtection TRansformer) используют механизм внимания, позволяющий моделировать глобальные зависимости между пикселями, что особенно полезно для точного определения границ объектов.
Обнаружение объектов в реальном времени представляет собой особый вызов – системы должны быть не только точными, но и достаточно быстрыми для обработки видеопотока. Именно здесь YOLO демонстрирует свою силу, предлагая оптимальный баланс между скоростью и точностью. В сочетании с библиотекой компьютерного зрения OpenCV, YOLO становится мощным инструментом для разработки практических приложений. Современные технологии обнаружения объектов в реальном времени находятся на пересечении теоретической глубины нейронных сетей и практических требований конкретных задач. Разработчикам приходится балансировать между точностью, скоростью обработки и использованием вычислителных ресурсов, особенно при развертывании на мобильных или встраеваемых устройствах.
Инструмент по распознованию объектов YOLO Доброго времени суток. Уважаемые форумчане, кто нибудь пользовался таким инструментом как YOLO для... CNN YOLO Добрый, нужен человек, который хорошо разбирается в CNN. Есть пару вопросов. Ничего готового не... Не запускается 'object-detection' в консоли Windows нейросети yolo Всем добрый день! Подскажите, почему при запуске команды в cmd Windows 'python yolo_opencv.py... Во время установки yolo v4 возникает ошибка по не нахождению hetopt.c Здравствуйте, во время сборки yolo_cpp_dll в visual studio возникает ошибка
C1083|Не удается...
Теоретические основы YOLO (You Only Look Once)
YOLO — настоящий прорыв в области детекции объектов. Его название "You Only Look Once" (смотришь только один раз) отражает ключевую идею: анализ изображения выполняется за единственный проход через нейронную сеть, что кардинально отличается от предшественников, требовавших множественных этапов обработки.
Архитектура и принцип работы
Основной принцип работы YOLO гениален в своей простоте. Входное изображение делится на сетку, обычно размером S×S (в YOLOv1 это 7×7). Каждая ячейка этой сетки отвечает за предсказание объектов, центр которых попадает в эту ячейку. Для каждой ячейки модель предсказывает:
1. B ограничивающих прямоугольников (bounding boxes) с координатами (x, y, w, h) и уверенностью (confidence).
2. C условных вероятностей классов P(Class|Object).
Это приводит к тензору предсказаний размерности S×S×(B×5+C), где 5 — это четыре координаты и одно значение уверенности для каждого bounding box.
При подаче изображения на вход сети оно масштабируется до фиксированного размера (обычно 416×416 или 608×608 пикселей). Затем сеть, состоящая из сверточных и полносвязных слоёв, генерирует предсказания для всей сетки одновременно. Финальная карта признаков интерпретируется как набор вероятностей и координат, которые после обработки превращаются в конкретные детекции объектов. Математически, для каждой ячейки сетки и каждого bounding box вычисляется итоговая вероятность:
P(Class_i|Object) × Confidence = P(Class_i) × IOU_truth_pred
где IOU_truth_pred — пересечение над объединением (Intersection Over Union) предсказанного прямоугольника и реального объекта.
Сравнение с другими алгоритмами
Сравнивая YOLO с другими подходами, нельзя не отметить его главное преимущество — скорость. В то время как двухэтапные детекторы типа Faster R-CNN сначала генерируют регионы-кандидаты, а потом классифицируют их, YOLO делает всё за один проход. Это напоминает разницу между решением задачи "в лоб" и элегантным алгоритмом: первый подход может быть точнее в отдельных случаях, но второй даёт потрясающий выигрыш в производительности. YOLO жертвует небольшой долей точности (особенно для маленьких объектов) в пользу существенного ускорения работы. Другие одноэтапные детекторы, такие как SSD (Single Shot MultiBox Detector), работают схожим образом, но используют различные архитектуры базовых сетей и стратегии предсказаний. YOLO отличается своей унифицированной архитектурой и подходом к обучению "от начала до конца" (end-to-end).
Разбор версий YOLO: эволюция алгоритма
История развития YOLO — это история постоянных улучшений и инноваций:
YOLOv1 (2015) — первая версия, представившая революционный подход. Использовала архитектуру, вдохновлённую GoogLeNet, и предсказывала фиксированное число боксов для каждой ячейки сетки. Основные ограничения: трудности с детекцией маленьких объектов и групп объектов.
YOLOv2/YOLO9000 (2016) — значительные улучшения точности при сохранении скорости. Ключевые инновации: введение якорных боксов (anchor boxes), использование пакетной нормализации, переход на полностью сверточную архитектуру (Darknet-19). YOLO9000 мог распознавать 9000 категорий объектов благодаря иерархическому классификатору.
YOLOv3 (2018) — дальнейшее улучшение точности, особенно для малых объектов. Использует более глубокую сеть Darknet-53 с остаточными связями (residual connections). Предсказывает боксы на трёх разных масштабах, что позволяет лучше обнаруживать объекты разных размеров.
YOLOv4 (2020) — оптимизирован для работы на графических процессорах. Ввёл множество архитектурных инноваций: CSPDarknet53 в качестве backbone, PANet для агрегации признаков, новые техники аугментации данных (Mosaic, CutMix) и функции активации (Mish).
YOLOv5 (2020) — реализация от Ultralytics на PyTorch, быстро стала популярной благодаря удобству использования и высокой производительности. Включает инструменты для экспорта моделей, квантизации и оптимизации для различных платформ.
YOLO-X, YOLO-R, YOLO-F и другие варианты — продолжают линейку, добавляя специфические улучшения для конкретных задач и платформ.
Каждая версия YOLO не просто увеличивала точность и скорость, но и вносила фундаментальные изменения в архитектуру и подход к обучению. Это делает семейство YOLO одним из самых динамично развивающихся в области компьютерного зрения.
Якорные боксы и механизм предсказания
Начиная с YOLOv2, в алгоритм был введён механизм якорных боксов (anchor boxes) — заранее определённых шаблонов прямоугольников различных размеров и пропорций. Вместо прямого предсказания координат, сеть учится предсказывать отклонения от этих шаблонов, что значительно упрощает задачу регрессии. Якорные боксы обычно определяются путём кластеризации размеров реальных объектов из обучающего набора данных (например, с помощью алгоритма k-means). Это позволяет априори задать наиболее вероятные формы объектов, которые модель будет искать.
Для каждого якорного бокса сеть предсказывает пять параметров: tx, ty, tw, th и to, где первые четыре определяют геометрию окончательного bounding box, а пятый — уверенность в наличии объекта. Фактические координаты центра бокса (bx, by) и его размеры (bw, bh) вычисляются по формулам:
bx = σ(tx) + cx
by = σ(ty) + cy
bw = pw * e^tw
bh = ph * e^th
где (cx, cy) — координаты левого верхнего угла текущей ячейки сетки, а (pw, ph) — ширина и высота соответствующего якорного бокса.
Сигмоидная функция σ() гарантирует, что смещение центра останется в пределах ячейки, что стабилизирует процесс обучения. А экспоненциальная трансформация для ширины и высоты обеспечивает положительные значения размеров.
Метрики оценки качества работы детекторов
Оценка производительности детекторов объектов — это многогранная задача, требующая учёта как точности обнаружения, так и скорости работы. Основные метрики включают:
IoU (Intersection over Union) — фундаментальная метрика, измеряющая перекрытие между предсказанным и фактическим ограничивающим прямоугольником. Вычисляется как отношение площади пересечения к площади объединения:
IoU = Area(Intersection) / Area(Union)
Обычно детекция считается верной, если IoU превышает определённый порог (часто 0.5 или 0.75).
Precision (точность) — доля правильных детекций среди всех предсказанных боксов:
Precision = TP / (TP + FP)
где TP (True Positives) — количество корректных детекций, а FP (False Positives) — количество ложных срабатываний.
Recall (полнота) — доля обнаруженных объектов среди всех реальных объектов:
Recall = TP / (TP + FN)
где FN (False Negatives) — количество пропущенных объектов.
AP (Average Precision) — площадь под кривой Precision-Recall для определённого класса объектов и порога IoU.
mAP (mean Average Precision) — среднее значение AP по всем классам. Это наиболее комплексная метрика, учитывающая способность детектора находить объекты всех классов с высокой точностью. На практике часто используют mAP@0.5 (при пороге IoU = 0.5) или mAP@[.5:.95] (усреднение по разным порогам IoU от 0.5 до 0.95 с шагом 0.05).
FPS (Frames Per Second) — количество кадров, обрабатываемых в секунду. Критическая метрика для приложений реального времени. Модификации YOLO обычно достигают от 30 до 100+ FPS на современных GPU, что делает их пригодными для обработки видеопотока.
Интересно, что разные версии YOLO имеют разные точки на кривой компромисса между скоростью и точностью. Например, YOLOv3-tiny жертвует точностью ради возможности работы на слабом оборудовании, а YOLOv4-CSP фокусируется на максимальной точности для высокоответственных задач.
Особенности обучения YOLO
Обучение YOLO — это искусство балансирования между множеством факторов. Функция потерь YOLO объединяет несколько компонентов:
1. Потеря локализации — ошибка в предсказании координат и размеров боксов.
2. Потеря уверенности — ошибка в предсказании вероятности наличия объекта.
3. Потеря классификации — ошибка в определении класса объекта.
В ранних версиях YOLO использовалась среднеквадратичная ошибка для всех трёх компонентов. В современных реализациях часто применяют комбинацию бинарной кросс-энтропии для уверенности и классификации, а для координат — модифицированную L1 или L2 норму с учётом размера объекта (GIoU, DIoU, CIoU-loss).
Важной особенностью является разное взвешивание компонентов потерь. Обычно координатным ошибкам придаётся больший вес, чем ошибкам классификации, поскольку точная локализация является первостепенной задачей детекции.
Аугментация данных играет ключевую роль в обучении современных версий YOLO. Помимо классических преобразований (поворот, масштабирование, обрезка), применяются:
Mosaic — объединение четырёх изображений в одно,
CutMix — случайное вырезание и вставка фрагментов между разными изображениями,
MixUp — смешивание пар изображений с разными весами.
Эти техники значительно увеличивают разнообразие обучающих данных, делая модель устойчивой к различным условиям съёмки и появлению объектов в необычных контекстах.
Выбор гиперпараметров критически важен для успешного обучения. Основные настройки включают:
Размер батча — влияет на стабильность градиентов, обычно от 8 до 64,
Скорость обучения — часто используется One Cycle Policy с разогревом и последующим снижением,
Регуляризация — L2-регуляризация весов и Dropout для предотвращения переобучения,
Параметры якорных боксов — подбираются под конкретный набор данных.
Интересный нюанс заключается в обработке "сложных" примеров. В современных версиях YOLO часто применяют Focal Loss или его модификации, которые автоматически уделяют больше внимания сложным для классификации объектам, игнорируя легкие примеры.
В обучении YOLO также важны техники, улучшающие обобщающую способность. Например, DropBlock — структурированная версия Dropout, которая случайно маскирует целые области карт признаков, заставляя сеть меньше полагаться на конкретные части объектов. Таким образом, YOLO представляет собой не просто архитектуру нейронной сети, а целую экосистему методов и приёмов, направленных на достижение оптимального баланса между точностью, скоростью и возможностью обобщения на новые данные.
Настройка среды разработки
Перед погружением в мир обнаружения объектов с YOLO и OpenCV необходимо настроить рабочую среду — не самая захватывающая, но важная часть процесса. Правильная настройка позволит избежать множества неприятных сюрпризов в будущем и обеспечит максимальную производительность системы.
Установка необходимых библиотек
Начнём с базовых компонентов. Python — очевидный выбор в качестве основного языка, но вместо системной версии лучше создать изолированную среду:
Bash | 1
2
3
4
5
6
7
8
| # Через conda
conda create -n yolo_env python=3.9
conda activate yolo_env
# Или через virtualenv
python -m venv yolo_env
source yolo_env/bin/activate # На Linux/Mac
yolo_env\Scripts\activate # На Windows |
|
Теперь установим основные библиотеки:
Bash | 1
2
3
4
5
6
7
8
| # OpenCV для обработки изображений
pip install opencv-python opencv-contrib-python
# Фреймворк для глубокого обучения
pip install torch torchvision torchaudio
# Дополнительные инструменты
pip install numpy matplotlib pillow tqdm requests |
|
Для YOLOv5 и новее процесс ещё проще:
Bash | 1
| pip install ultralytics |
|
Если же вы предпочитаете оригинальную реализацию YOLOv3/v4, придётся скомпилировать Darknet — фреймворк на C, созданный разработчиком YOLO Джозефом Редмоном:
Bash | 1
2
3
4
5
6
7
8
9
| git clone https://github.com/AlexeyAB/darknet.git
cd darknet
# Правим Makefile для поддержки GPU и OpenCV
sed -i 's/OPENCV=0/OPENCV=1/' Makefile
sed -i 's/GPU=0/GPU=1/' Makefile
sed -i 's/CUDNN=0/CUDNN=1/' Makefile
make |
|
Компиляция может занять некоторое время и потребовать дополнительных зависимостей типа компилятора GCC, утилит сборки и заголовочных файлов.
Альтернативные конфигурации: Docker-контейнеры для быстрого развёртывания
Контейнеризация обеспечивает идентичную среду выполнения независимо от того, где запускается ваш код.
Для YOLO существуют готовые образы:
Bash | 1
2
3
| # Запуск контейнера с YOLOv5
docker pull ultralytics/yolov5
docker run --ipc=host -it ultralytics/yolov5 |
|
Для более специфичных требований создадим собственный Dockerfile:
Code | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| FROM nvidia/cuda:11.4.0-cudnn8-devel-ubuntu20.04
# Базовые зависимости
RUN apt-get update && apt-get install -y \
python3-pip \
python3-dev \
libgl1-mesa-glx \
libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
# Python-пакеты
COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt
# Рабочая директория
WORKDIR /app
COPY . /app
# Команда по умолчанию
CMD ["python3", "detect.py"] |
|
При запуске нужно правильно пробросить видеоустройства и GPU:
Bash | 1
2
3
4
5
| docker run -it --gpus all -v $(pwd):/app \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY=$DISPLAY \
--device=/dev/video0:/dev/video0 \
yolo_opencv_image |
|
Конфигурация YOLO и OpenCV
Загрузим предобученные модели YOLO. Для YOLOv3:
Bash | 1
2
3
| wget https://pjreddie.com/media/files/yolov3.weights
wget https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg
wget https://raw.githubusercontent.com/pjreddie/darknet/master/data/coco.names |
|
Интеграция с OpenCV происходит через модуль dnn :
Python | 1
2
3
4
5
6
7
8
9
10
11
| import cv2
import numpy as np
# Загрузка YOLO
net = cv2.dnn.readNet("yolov3.weights", "yolov3.cfg")
layer_names = net.getLayerNames()
output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()]
# Загрузка классов
with open("coco.names", "r") as f:
classes = [line.strip() for line in f.readlines()] |
|
Многие новички совершают ошибку, не анализируя файл конфигурации. А ведь это ключевой элемент для тонкой настройки! Например, в yolov3.cfg можно найти секции [yolo] с параметрами якорных боксов и другими настройками слоёв обнаружения.
Выбор и подготовка датасетов для обучения и тестирования
Хорошему детектору нужны хорошие данные. Популярные открытые датасеты:
COCO — большой и разнообразный датасет с 80 классами объектов,
Pascal VOC — классика с 20 категориями,
Open Images — гигантский набор от Google с 600 классами,
BDD100K — специализированный автомобильный датасет для сценариев вождения.
Для собственных задач часто нужно создавать кастомный датасет. YOLO ожидает особый формат:
1. Изображения (.jpg/.png) в директории images/ .
2. Разметка (.txt) в директории labels/ .
Каждый txt-файл содержит строки вида:
Python | 1
| <class_id> <x_center> <y_center> <width> <height> |
|
Числа нормализованы в диапазоне [0,1], что делает их независимыми от разрешения изображения. Это хитрая идея, но порождает множество путаницы у начинающих. Конвертиры из других форматов (COCO, VOC) неизбежно потребуются в практической работе. Существует множество скриптов для этого, но следует убедиться, что они правильно обрабатывают краевые случаи.
Аугментация — еще один недооценённый аспект подготовки данных. Ротации, изменения яркости, добавление шума — всё это помогает модели стать более устойчивой. С библиотекой Albumentations это делается в несколько строк:
Python | 1
2
3
4
5
| transform = A.Compose([
A.RandomBrightnessContrast(p=0.5),
A.HorizontalFlip(p=0.5),
A.Rotate(limit=10, p=0.5)
], bbox_params=A.BboxParams(format='yolo', label_fields=['class_labels'])) |
|
Конфигурация GPU-ускорения для обработки в реальном времени
Обнаружение объектов в реальном времени без ускорителей почти нереально, разве что у вас очень мощный процессор. CUDA и cuDNN — стандарт для GPU-ускорения.
Проверьте, видит ли система ваш GPU:
Python | 1
2
3
| print(f"CUDA доступен: {torch.cuda.is_available()}")
print(f"Количество GPU: {torch.cuda.device_count()}")
print(f"Название устройства: {torch.cuda.get_device_name(0)}") |
|
Для использования GPU в OpenCV:
Python | 1
2
| net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA) |
|
Если вы работаете с TensorFlow, конфигурация немного отличается:
Python | 1
2
3
4
5
| import tensorflow as tf
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True) |
|
Для некоторых старых GPU или малобюджетных систем половинная точность (FP16) может обеспечить значительное ускорение при незначительном снижении точности:
Python | 1
| net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA_FP16) |
|
Тестирование производительности на различных платформах
После настройки среды разумно провести бенчмарк — быстро поймёте, на что способна ваша система:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| def benchmark(net, input_size=(416, 416), iterations=100):
# Создаём случайное изображение для теста
img = np.random.random((input_size[0], input_size[1], 3))
img = (img * 255).astype(np.uint8)
# Разогрев
blob = cv2.dnn.blobFromImage(img, 1/255.0, input_size, swapRB=True)
net.setInput(blob)
net.forward(output_layers)
# Измерение времени
start = time.time()
for _ in range(iterations):
blob = cv2.dnn.blobFromImage(img, 1/255.0, input_size, swapRB=True)
net.setInput(blob)
net.forward(output_layers)
elapsed = time.time() - start
fps = iterations / elapsed
return fps |
|
Ориентировочные результаты (для YOLOv4, размер входа 416×416):
NVIDIA RTX 3080: ~90 FPS
NVIDIA GTX 1660: ~45 FPS
Intel Core i7 (только CPU): ~3-5 FPS
Raspberry Pi 4: менее 1 FPS
Для встраиваемых систем типа Raspberry Pi или Jetson Nano обязательно применяйте оптимизации:
1. Используйте легковесные модели (YOLOv5s-tiny, YOLOv4-tiny).
2. Снижайте разрешение входа (например, до 320×320).
3. Конвертируйте модели в оптимизированные форматы (ONNX, TensorRT).
4. При необходимости применяйте квантизацию (INT8).
Облачные решения (AWS, Google Cloud, Azure) открывают другие возможности — распределение нагрузки между несколькими серверами и масштабирование в зависимости от потребностей.
Со всеми этими настройками вы готовы начать практическую работу с YOLO и OpenCV для обнаружения объектов в реальном времени. Теперь мы перейдём к реализации самого детектора и созданию полноценного приложения компьютерного зрения.
Практическая реализация
После подготовки теоретической базы и настройки окружения переходим к самому интересному — написанию кода для обнаружения объектов в реальном времени. Реализация YOLO с OpenCV требует последовательного подхода, от базовых компонентов к более сложным оптимизациям.
Базовый код для видеопотока
Начнём с создания простейшего детектора объектов, работающего с видеопотоком:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
| import cv2
import numpy as np
import time
# Загрузка YOLO
net = cv2.dnn.readNet("yolov3.weights", "yolov3.cfg")
with open("coco.names", "r") as f:
classes = [line.strip() for line in f.readlines()]
layer_names = net.getLayerNames()
output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()]
# Открываем видеопоток
cap = cv2.VideoCapture(0) # 0 для веб-камеры, или путь к видеофайлу
while True:
ret, frame = cap.read()
if not ret:
break
height, width, channels = frame.shape
# Создаём blob из кадра
blob = cv2.dnn.blobFromImage(frame, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
# Пропускаем blob через сеть
net.setInput(blob)
start = time.time()
outs = net.forward(output_layers)
end = time.time()
# Информация для отображения
class_ids = []
confidences = []
boxes = []
# Обработка выходных данных сети
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > 0.5:
# Координаты объекта
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
# Прямоугольные координаты
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
# Применение Non-Maximum Suppression
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
# Отрисовка результатов
for i in range(len(boxes)):
if i in indexes:
x, y, w, h = boxes[i]
label = str(classes[class_ids[i]])
confidence = confidences[i]
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(frame, f"{label} {confidence:.2f}", (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# Отображение FPS
cv2.putText(frame, f"FPS: {1/(end-start):.2f}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
cv2.imshow("Object Detection", frame)
if cv2.waitKey(1) == 27: # ESC для выхода
break
cap.release()
cv2.destroyAllWindows() |
|
Этот код выполняет основные шаги: загружает модель, обрабатывает видеопоток, преобразует кадры в формат, приемлемый для нейросети, пропускает через модель и визуализирует результаты. Однако реальные приложения требуют дополнительных улучшений.
Предобработка входных данных для улучшения точности
Качество входных данных напрямую влияет на точность детекции. Простейший случай — нормализация изображения — уже включён в базовый код через параметр blobFromImage . Но можно пойти дальше:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
| def preprocess_image(image):
# Удаляем шум с помощью билатерального фильтра (сохраняет края)
denoised = cv2.bilateralFilter(image, 9, 75, 75)
# Улучшаем контраст с помощью CLAHE
lab = cv2.cvtColor(denoised, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl = clahe.apply(l)
enhanced_lab = cv2.merge((cl, a, b))
enhanced = cv2.cvtColor(enhanced_lab, cv2.COLOR_LAB2BGR)
return enhanced |
|
Такая предобработка особенно полезна при работе в сложных условиях: недостаточное освещение, зашумленная или низкоконтрастная картинка. Изящный ход — адаптивно применять разные методы предобработки в зависимости от характеристик кадра.
Настройка параметров обнаружения
Тонкая настройка порогов уверенности и NMS имеет решающее значение для баланса между большим количеством ложных срабатываний и пропуском объектов:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| def process_detections(outputs, confidence_threshold=0.5, nms_threshold=0.4):
class_ids = []
confidences = []
boxes = []
for out in outputs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > confidence_threshold:
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
# Применение Non-Maximum Suppression
indexes = cv2.dnn.NMSBoxes(boxes, confidences, confidence_threshold, nms_threshold)
return boxes, confidences, class_ids, indexes |
|
В зависимости от ситуации значения порогов могут сильно различаться:- Для критичных систем безопасности предпочтение отдаётся высокой полноте (низкий порог уверенности ~0.3).
- Для автоматической аннотации данных важна высокая точность (высокий порог ~0.7).
- Для NMS порог 0.4-0.5 обычно дает хороший баланс.
Трюк опытных разработчиков — делать эти пороги настраиваемыми через UI в конечном приложении, позволяя пользователю адаптировать систему под конкретную сцену.
Мультипоточное программирование для параллельной обработки
Для достижения действительно высокой производительности нужно разделить захват видео, обработку и отображение на отдельные потоки:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| import threading
import queue
class VideoStreamProcessor:
def __init__(self, source=0):
self.capture = cv2.VideoCapture(source)
self.frame_queue = queue.Queue(maxsize=2)
self.result_queue = queue.Queue(maxsize=2)
self.stopped = False
def start(self):
# Поток для захвата кадров
capture_thread = threading.Thread(target=self._capture_loop)
capture_thread.daemon = True
capture_thread.start()
# Поток для обработки кадров
process_thread = threading.Thread(target=self._process_loop)
process_thread.daemon = True
process_thread.start()
return self
def _capture_loop(self):
while not self.stopped:
if not self.frame_queue.full():
ret, frame = self.capture.read()
if ret:
self.frame_queue.put(frame)
else:
self.stopped = True
else:
time.sleep(0.01) # Небольшая задержка, чтобы не загружать CPU
def _process_loop(self):
while not self.stopped:
if not self.frame_queue.empty() and not self.result_queue.full():
frame = self.frame_queue.get()
# Здесь вся обработка YOLO
# ...
# Результат помещаем в очередь вывода
self.result_queue.put(processed_frame)
else:
time.sleep(0.01)
def read(self):
if self.result_queue.empty():
return False, None
return True, self.result_queue.get()
def stop(self):
self.stopped = True |
|
Такая архитектура позволяет максимально использовать многоядерные процессоры и предотвращает блокировки UI при обработке сложных кадров. При этом стоит учесть потенциальные проблемы синхронизации между потоками и непредсказуемую латентность.
Методы фильтрации и стабилизации детекций
Одна из главных проблем детекции в видеопотоке — "дрожание" ограничивающих рамок от кадра к кадру. Применение фильтра Калмана дает отличные результаты:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| class BoxFilter:
def __init__(self, process_noise=0.01, measurement_noise=0.1):
self.kalman = cv2.KalmanFilter(8, 4)
# Матрица перехода состояния (F)
self.kalman.transitionMatrix = np.array([
[1, 0, 0, 0, 1, 0, 0, 0], # x
[0, 1, 0, 0, 0, 1, 0, 0], # y
[0, 0, 1, 0, 0, 0, 1, 0], # w
[0, 0, 0, 1, 0, 0, 0, 1], # h
[0, 0, 0, 0, 1, 0, 0, 0], # dx
[0, 0, 0, 0, 0, 1, 0, 0], # dy
[0, 0, 0, 0, 0, 0, 1, 0], # dw
[0, 0, 0, 0, 0, 0, 0, 1] # dh
], np.float32)
# Матрица измерения (H)
self.kalman.measurementMatrix = np.array([
[1, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0]
], np.float32)
# Установка шума процесса и измерения
self.kalman.processNoiseCov = np.eye(8, dtype=np.float32) * process_noise
self.kalman.measurementNoiseCov = np.eye(4, dtype=np.float32) * measurement_noise
self.initialized = False
def update(self, bbox):
x, y, w, h = bbox
if not self.initialized:
self.kalman.statePre = np.array([[x], [y], [w], [h], [0], [0], [0], [0]], np.float32)
self.kalman.statePost = self.kalman.statePre.copy()
self.initialized = True
# Предсказание
prediction = self.kalman.predict()
# Обновление с помощью измерения
measurement = np.array([[x], [y], [w], [h]], np.float32)
corrected = self.kalman.correct(measurement)
# Возвращаем сглаженные координаты
return [int(corrected[0]), int(corrected[1]), int(corrected[2]), int(corrected[3])] |
|
Для эффективной фильтрации мелких "дрожаний" можно также применить простое скользящее среднее. Такие методы особенно полезны при создании интерактивных приложений, где плавность отображения критична для пользовательского опыта.
Идеальное решение часто включает комбинацию нескольких подходов:
1. Временное сглаживание с использованием фильтра Калмана.
2. Пространственную согласованность через сравнение пересечений боксов в последовательных кадрах.
3. Экспоненциальное взвешенное среднее для скоростей и размеров объектов.
Эти методы не только улучшают визуальное восприятие результатов, но и повышают точность систем, использующих выходные данные детектора для дальнейшего анализа.
Реализация трекинга обнаруженных объектов
Фильтрация детекций — только первый шаг к созданию полноценной системы. Настоящий вызов — трекинг объектов, позволяющий присваивать уникальные идентификаторы и отслеживать историю перемещения. Алгоритмы SORT (Simple Online and Realtime Tracking) и DeepSORT предлагают элегантные решения этой задачи. Базовая реализация SORT выглядит так:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
| from scipy.optimize import linear_sum_assignment
class Sort:
def __init__(self, max_age=10, min_hits=3, iou_threshold=0.3):
self.max_age = max_age
self.min_hits = min_hits
self.iou_threshold = iou_threshold
self.trackers = []
self.frame_count = 0
self.next_id = 1
def update(self, detections):
self.frame_count += 1
# Предсказание новых положений трекеров
predicted_boxes = []
for tracker in self.trackers:
predicted_box = tracker.predict()
predicted_boxes.append(predicted_box)
# Создаём матрицу IoU между детекциями и предсказаниями
iou_matrix = np.zeros((len(detections), len(predicted_boxes)))
for d, detection in enumerate(detections):
for t, prediction in enumerate(predicted_boxes):
iou_matrix[d, t] = self._iou(detection, prediction)
# Используем Венгерский алгоритм для назначения детекций трекерам
row_indices, col_indices = linear_sum_assignment(-iou_matrix)
matched_indices = np.array(list(zip(row_indices, col_indices)))
# Обновление трекеров с соответствующими детекциями
for m in matched_indices:
if iou_matrix[m[0], m[1]] < self.iou_threshold:
continue
self.trackers[m[1]].update(detections[m[0]])
# Создаём новые трекеры для необработанных детекций
unmatched_detections = [i for i in range(len(detections))
if i not in matched_indices[:, 0]]
for i in unmatched_detections:
self.trackers.append(KalmanBoxTracker(detections[i], self.next_id))
self.next_id += 1
# Удаляем трекеры, которые не обновлялись длительное время
self.trackers = [t for t in self.trackers if t.time_since_update < self.max_age]
# Формируем и возвращаем результаты
results = []
for tracker in self.trackers:
if tracker.hits >= self.min_hits:
results.append((tracker.id, tracker.get_state()))
return results
def _iou(self, box1, box2):
# Реализация вычисления IoU между двумя боксами
x1, y1, w1, h1 = box1
x2, y2, w2, h2 = box2
xi1 = max(x1, x2)
yi1 = max(y1, y2)
xi2 = min(x1 + w1, x2 + w2)
yi2 = min(y1 + h1, y2 + h2)
if xi2 <= xi1 or yi2 <= yi1:
return 0.0
intersection = (xi2 - xi1) * (yi2 - yi1)
box1_area = w1 * h1
box2_area = w2 * h2
return intersection / (box1_area + box2_area - intersection) |
|
DeepSORT улучшает базовый SORT, добавляя визуальные особенности для более надёжного сопоставления. Это особенно полезно в сценариях с пересечениями и временными окклюзиями объектов.
Интеграция трекера в основной цикл обработки видео:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| # В основном цикле обработки
tracker = Sort()
while True:
ret, frame = cap.read()
if not ret:
break
# Обработка YOLO (как в предыдущем коде)
# ...
# Преобразуем боксы в формат [x, y, w, h]
detections = []
for i in indexes:
i = i[0]
detections.append(boxes[i])
# Обновляем трекер
tracked_objects = tracker.update(detections)
# Отрисовка результатов с уникальными ID
for obj_id, box in tracked_objects:
x, y, w, h = [int(v) for v in box]
color = get_color_by_id(obj_id) # Функция, возвращающая уникальный цвет для ID
cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
cv2.putText(frame, f"ID: {obj_id}", (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
cv2.imshow("Tracking", frame)
# ... |
|
Оптимизация производительности
Даже самый точный детектор бесполезен, если он не успевает обрабатывать кадры в реальном времени. Вот несколько проверенных приемов оптимизации:
1. Операции предварительной обработки на GPU. Перенос масштабирования и нормализации изображений на GPU может дать заметное ускорение:
Python | 1
2
3
4
5
6
7
8
| # Подготовка данных на GPU
if cv2.cuda.getCudaEnabledDeviceCount() > 0:
gpu_frame = cv2.cuda_GpuMat()
gpu_frame.upload(frame)
gpu_frame_resized = cv2.cuda.resize(gpu_frame, (416, 416))
preprocessed = gpu_frame_resized.download()
else:
preprocessed = cv2.resize(frame, (416, 416)) |
|
2. Пропуск кадров (frame skipping). В системах с ограниченными ресурсами можно обрабатывать не каждый кадр:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
| frame_counter = 0
process_every_n_frames = 2
while True:
ret, frame = cap.read()
frame_counter += 1
if frame_counter % process_every_n_frames != 0:
continue
# Обычная обработка
# ... |
|
3. Динамическое изменение разрешения. Адаптация к текущей нагрузке:
Python | 1
2
3
4
5
6
7
8
| def adaptive_resolution(frame, target_fps=30, current_fps=None):
if current_fps is None or current_fps >= target_fps:
return cv2.resize(frame, (416, 416))
# Если FPS ниже целевого, уменьшаем разрешение
scale = max(0.5, min(1.0, current_fps / target_fps))
new_size = (int(416 * scale), int(416 * scale))
return cv2.resize(frame, new_size) |
|
4. Region of Interest (ROI). Если камера статична, можно обрабатывать только значимые области:
Python | 1
2
3
4
5
6
7
| # Определение маски интереса
roi_mask = np.zeros(frame.shape[:2], dtype=np.uint8)
roi_points = np.array([[100, 100], [100, 400], [500, 400], [500, 100]])
cv2.fillPoly(roi_mask, [roi_points], 255)
# Применение маски
masked_frame = cv2.bitwise_and(frame, frame, mask=roi_mask) |
|
Обработка результатов
Обнаружение объектов само по себе редко бывает конечной целью. Обычно результаты анализируются для принятия решений или сохранения статистики. Реализация подсчёта объектов, пересекающих виртуальную линию:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| class LineCounter:
def __init__(self, line_start, line_end):
self.line_start = line_start # (x1, y1)
self.line_end = line_end # (x2, y2)
self.counts = {}
self.crossed_ids = set()
def update(self, tracked_objects):
for obj_id, box in tracked_objects:
x, y, w, h = box
center_x = x + w // 2
center_y = y + h // 2
if self._point_crossed_line((center_x, center_y)) and obj_id not in self.crossed_ids:
self.crossed_ids.add(obj_id)
if obj_id in self.counts:
self.counts[obj_id] += 1
else:
self.counts[obj_id] = 1
def _point_crossed_line(self, point):
# Проверка, пересекает ли точка линию
# Здесь можно использовать более сложную логику,
# учитывающую направление движения
x, y = point
x1, y1 = self.line_start
x2, y2 = self.line_end
# Расстояние от точки до линии
distance = abs((y2-y1)*x - (x2-x1)*y + x2*y1 - y2*x1) / \
np.sqrt((y2-y1)[B]2 + (x2-x1)[/B]2)
return distance < 5 # Порог для определения пересечения |
|
Для долгосрочного анализа полезно сохранять результаты в базу данных или CSV-файл:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| import csv
from datetime import datetime
def log_detections(detections, filename="detections_log.csv"):
with open(filename, 'a', newline='') as csvfile:
fieldnames = ['timestamp', 'object_id', 'class', 'confidence', 'x', 'y', 'w', 'h']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
if csvfile.tell() == 0: # Если файл пуст, пишем заголовки
writer.writeheader()
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
for obj_id, (x, y, w, h), class_id, confidence in detections:
writer.writerow({
'timestamp': timestamp,
'object_id': obj_id,
'class': class_names[class_id],
'confidence': confidence,
'x': x, 'y': y, 'w': w, 'h': h
}) |
|
Визуализация траекторий движения часто даёт ценную информацию о паттернах поведения:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| class TrajectoryVisualizer:
def __init__(self, max_history=30):
self.trajectories = {}
self.max_history = max_history
def update(self, tracked_objects):
for obj_id, box in tracked_objects:
x, y, w, h = box
center = (x + w // 2, y + h // 2)
if obj_id not in self.trajectories:
self.trajectories[obj_id] = []
self.trajectories[obj_id].append(center)
# Ограничиваем историю
if len(self.trajectories[obj_id]) > self.max_history:
self.trajectories[obj_id].pop(0)
def draw(self, frame):
for obj_id, trajectory in self.trajectories.items():
if len(trajectory) < 2:
continue
color = get_color_by_id(obj_id)
# Рисуем линии траектории
for i in range(1, len(trajectory)):
thickness = int(np.sqrt(self.max_history / float(i + 1)) * 2.5)
cv2.line(frame, trajectory[i-1], trajectory[i], color, thickness)
return frame |
|
Проблемы и решения в реальных проектах
Теория и лабораторные эксперименты — лишь первый шаг. При внедрении систем обнаружения объектов в реальных условиях неизбежно возникают вызовы, с которыми не сталкиваешься в идеальной среде. Рассмотрим наиболее распространённые проблемы и способы их решения.
Типичные ошибки и ограничения
Системы на основе YOLO, при всей их эффективности, имеют характерные слабости:
1. Сложности с маленькими объектами. YOLO, особенно ранние версии, известен проблемами с обнаружением малоразмерных объектов. Это связано с дискретизацией выходной сетки и структурой потери информации при даунсэмплинге.
Решение: Использование мультимасштабного подхода с предсказаниями на разных уровнях сети (как в YOLOv3+) и увеличение разрешения входного изображения.
2. Объекты в плотных группах. Когда объекты располагаются близко друг к другу, NMS может удалить валидные детекции.
Решение: Настройка параметров NMS для конкретной задачи или замена стандартного NMS на Soft-NMS, который не полностью отбрасывает перекрывающиеся боксы, а снижает их уверенность пропорционально степени перекрытия.
3. Необычные ракурсы и позы. Объекты в непривычных положениях или с нестандартными пропорциями обнаруживаются хуже.
Решение: Обогащение обучающего набора данных изображениями с разнообразными ракурсами и использование сильной аугментации при обучении.
Исследование, проведённое группой из MIT, показало, что добавление поворотно-инвариантного слоя может повысить точность обнаружения объектов в нестандартных позах на 12-18%.
Работа с нестандартными объектами и сложными сценами
Реальность часто преподносит сюрпризы в виде сложных условий:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| # Адаптивная настройка контраста для сложных сцен
def enhance_scene(frame, brightness_threshold=120):
# Оцениваем среднюю яркость
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
avg_brightness = np.mean(gray)
# Для тёмных сцен повышаем яркость и контраст
if avg_brightness < brightness_threshold:
# Конвертируем в HSV для работы с яркостью
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
# Увеличиваем яркость
v = cv2.add(v, 30)
# Повышаем контраст
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
v = clahe.apply(v)
# Собираем изображение обратно
hsv = cv2.merge([h, s, v])
return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
return frame |
|
Для объектов, которых нет в стандартных датасетах, понадобится дообучение. Типичная ловушка — использование слишком малого набора данных, что приводит к переобучению. Практика показывает, что для стабильных результатов нужно минимум 50-100 качественно размеченных изображений на класс.
Техники дообучения и интеграции
Fine-tuning на пользовательских данных — мощный инструмент, позволяющий адаптировать предобученные модели под специфические задачи:
Python | 1
2
3
| # Пример дообучения YOLOv5 (с использованием Ultralytics)
!python train.py --img 640 --batch 16 --epochs 50 --data custom.yaml \
--weights yolov5s.pt --cache |
|
Ключевые принципы успешного дообучения:- Начинайте с малой скорости обучения (1e-4 или меньше).
- Замораживайте ранние слои сети для сохранения обобщающей способности.
- Используйте ранний останов (early stopping) для предотвращения переобучения.
Интеграция с другими системами требует внимания к форматам данных и протоколам обмена. Для построения комплексного пайплайна часто используются очереди сообщений (RabbitMQ, Kafka) и RESTful API:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| # Минималистичный REST API для обработки изображений
from flask import Flask, request, jsonify
import base64
import cv2
import numpy as np
app = Flask(__name__)
detector = YoloDetector() # Наш класс детектора
@app.route('/detect', methods=['POST'])
def detect_objects():
# Получаем изображение из запроса
image_data = request.json.get('image')
image_bytes = base64.b64decode(image_data)
# Конвертируем в формат OpenCV
nparr = np.frombuffer(image_bytes, np.uint8)
image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# Обнаруживаем объекты
detections = detector.detect(image)
# Возвращаем результаты в JSON
return jsonify({'detections': detections})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000) |
|
Оптимизация для слабого оборудования
Для развёртывания на устройствах с ограниченными ресурсами критически важны методы сжатия моделей:
1. Квантизация — переход от 32-битных весов к 8-битным или даже бинарным.
2. Прунинг — удаление малозначимых связей в нейронной сети.
3. Дистилляция знаний — обучение компактной студенческой модели на выходах большой учительской.
TensorRT предоставляет мощный набор инструментов для оптимизации:
Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # Конвертация модели в TensorRT
import tensorrt as trt
logger = trt.Logger(trt.Logger.WARNING)
builder = trt.Builder(logger)
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
parser = trt.OnnxParser(network, logger)
# Парсим ONNX-модель
with open('yolov3.onnx', 'rb') as model:
if not parser.parse(model.read()):
print('ERROR: Failed to parse ONNX model')
for error in range(parser.num_errors):
print(parser.get_error(error))
# Создаём оптимизированный TensorRT-движок
config = builder.create_builder_config()
config.max_workspace_size = 1 << 28 # 256 МБ
engine = builder.build_engine(network, config) |
|
На Raspberry Pi 4 замена YOLOv4 на YOLOv4-tiny с последующей квантизацией может увеличить FPS с <1 до приемлемых 5-7.
С практической точки зрения, нередко приходится жертвовать точностью ради скорости или наоборот. Секрет успешного проекта — найти баланс, учитывающий специфические требования конкретной задачи и ограничения целевого оборудования.
Добраться до свойств YOLO-NAS Всем доброго дня.
Хочу поменять свойства слоя в сети YOLO-NAS. Фреймворк для работы с ней -... YOLO. В чем разница model() и model.predict()? в чем разница в вызове метода в Python (код для YOLO):
from ultralytics import YOLO
model =... YOLO + PyCharm. Зависает на обучении модели Доброго дня!
Решил попробовать YOLOv8 (Win10 + PyCharm)
from ultralytics import YOLO
... yolo при обучении на своем датасете, в чем разница с нуля и pretrained model? yolo 11
при обучении на своем датасете, в чем разница строить модель с нуля "yolo11n.yaml" и... Opencv error the function/feature is not implemented (opencv was built without surf support) Недавно настроила OpenCV для CodeBlocks, однако первый пример поиска плоских объектов с помощью... Лабораторная bag-of-words image classification OpenCV 2.4 в OpenCV 3 Здравствуйте! Делал лабораторную с интуита по OpenCV, но там она для старой версии, а мне нужно в... Помощь с нейросеткой с opencv. Error in module: Name 'opencv' is not defined Ребят, привет. Признаюсь честно, я начинающий программист, и делал раньше в основном простые... Python c opencv использующий dll с cpp и opencv через ctypes и пустые окна Возможно мне стоило писать в тред по питону, заранее прошу прощения если ошибся.
У меня есть dll... OpenCV Всем доброго времени суток !!! :) ... - у меня такой вопрос - как с помощтю OpenCV - ну для начала... Кто работал с OpenCV? Вопрос в следующем. Нужно реализовать программу, которая будет выделять из изображение необходимый... Преобразованиие Хафа для линий через opencv #include <stdafx.h>
#include <cv.h>
#include <highgui.h>
#include <math.h>
int main(int argc,... с++ & OpenCV Ребят, срочно нужно разобраться с ошибкой:
1>LINK : fatal error LNK1104: cannot open file...
|