Как ускорить YOLOv8n при многопоточном запуске: советы по повышению FPS
Использование предобученной модели YOLOv8n для детекции объектов в реальном времени - популярная задача. Однако при стандартном запуске скорость может составлять около 30 кадров в секунду (FPS), а внедрение многопоточности не всегда даёт ожидаемый прирост. В этой статье разберём, почему потоковая обработка неэффективна и как действительно увеличить производительность детекции.
Почему многопоточность не ускоряет YOLO?
YOLO - это модель глубокого обучения, которая интенсивно использует GPU. Python-потоки (threading) работают в рамках GIL (Global Interpreter Lock), что ограничивает параллельное выполнение CPU-задач. Если детекция выполняется на GPU, потоки не дают выигрыша, так как GPU уже загружен одной задачей. В вашем коде поток detect постоянно вызывает модель, а основной поток читает кадры и рисует боксы - это приводит к гонке данных и блокировкам.
Основные узкие места в коде
Проанализируем код, чтобы найти причины низкой производительности:
- Глобальная переменная
frame: поток детекции и основной поток одновременно обращаются к кадру без синхронизации, что вызывает задержки. - Бесконечный цикл детекции: поток
detectработает без ожидания нового кадра, тратя ресурсы на повторную обработку одного и того же изображения. - Вызов
time.sleep: задержки в 0.0017 и 0.025 секунды искусственно снижают FPS. - Отрисовка в основном потоке: операции
cv2.rectangleиcv2.putTextвыполняются для каждого кадра, что добавляет нагрузку на CPU.
Как увеличить FPS: практические шаги
1. Используйте multiprocessing вместо threading
Для обхода GIL применяйте модуль multiprocessing. Это позволит запустить детекцию в отдельном процессе, который сможет полноценно использовать GPU. Однако будьте осторожны с передачей данных между процессами - используйте очереди (Queue) или разделяемую память.
2. Оптимизируйте размер входного кадра
Уменьшите разрешение видео перед подачей в модель. YOLOv8n работает быстрее на меньших изображениях (например, 320x320 или 416x416). Используйте cv2.resize перед вызовом model(frame).
3. Настройте параметры модели
YOLOv8n поддерживает аргументы для ускорения: model(frame, half=True) - включение FP16 (половинной точности) на современных GPU, device='cuda' - явное указание GPU, conf=0.5 - повышение порога уверенности для уменьшения числа боксов.
4. Используйте асинхронный захват кадров
Захват видео через cv2.VideoCapture может быть медленным. Создайте отдельный процесс для чтения кадров с веб-камеры и складывайте их в очередь. Основной процесс будет брать последний кадр и детектировать, не ожидая захвата.
5. Удалите лишние задержки и синхронизируйте потоки
Уберите вызовы time.sleep и используйте блокировки (threading.Lock) для безопасного доступа к общим данным. Лучше реализовать паттерн «производитель-потребитель» с очередью.
Пример оптимизированного подхода
Вместо глобальной переменной frame создайте очередь cv2.Queue (или multiprocessing.Queue). Поток захвата кладёт кадры в очередь, поток детекции берёт оттуда последний кадр и обрабатывает. Результаты детекции передаются обратно в основной поток для отрисовки. Это минимизирует простои и повышает FPS до 50-60.
Дополнительные советы
Попробуйте использовать cv2.CAP_DSHOW для DirectShow на Windows, чтобы ускорить захват. Также отключите отрисовку, если она не нужна в реальном времени - записывайте результаты в буфер и рисуйте реже. Если модель загружается с Hugging Face, кэшируйте веса локально.