Различные статьи по Qt и c++

Детекция объектов: Haar-каскады, HOG, основы deep learning (DNN-модуль OpenCV)

Введение

Статьи 565-567 разобрали базовые техники выделения объектов (по цвету, по форме контура, по характерным признакам) — в этой статье разберём специализированные, более продвинутые подходы к детекции объектов конкретных категорий (лица, люди, и произвольные объекты через нейросети): Haar-каскады, HOG-детектор, и модуль глубокого обучения OpenCV (dnn).

Концепция

Haar-каскады — классический, относительно старый (но всё ещё практически полезный для простых случаев, особенно детекции лиц) метод детекции объектов, основанный на последовательном применении множества простых классификаторов, каждый проверяющий наличие определённого, простого паттерна яркости в небольшой области изображения — OpenCV поставляется с готовыми, предварительно обученными каскадами для типичных задач (детекция лиц, глаз), не требующими собственного обучения. HOG (Histogram of Oriented Gradients) — другой классический метод, особенно известный в сочетании с SVM-классификатором для детекции людей на изображении, анализирующий распределение направлений градиентов яркости в локальных областях изображения. Модуль dnn OpenCV позволяет загружать и выполнять предобученные нейросетевые модели (созданные в популярных фреймворках глубокого обучения, как TensorFlow, PyTorch, ONNX) непосредственно через OpenCV API, без необходимости интегрировать сам исходный фреймворк глубокого обучения как отдельную, более тяжеловесную зависимость — это даёт доступ к значительно более точной, современной детекции широкого спектра объектов по сравнению с классическими методами Haar/HOG.

Пример кода: Детекция объектов

// Детекция лиц через готовый Haar-каскад
#include <opencv2/opencv.hpp>
#include <opencv2/objdetect.hpp>

int main()
{
    cv::CascadeClassifier faceDetector;
    if (!faceDetector.load("haarcascade_frontalface_default.xml")) { // готовый файл, поставляемый с OpenCV
        std::cerr << "Не удалось загрузить каскад" << std::endl;
        return 1;
    }

    cv::Mat image = cv::imread("photo_with_faces.jpg");
    cv::Mat gray;
    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);

    std::vector<cv::Rect> faces;
    faceDetector.detectMultiScale(gray, faces, 1.1, 5); // параметры влияют на точность/скорость детекции

    for (const auto &face : faces) {
        cv::rectangle(image, face, cv::Scalar(0, 255, 0), 2);
    }

    cv::imwrite("faces_detected.png", image);
    std::cout << "Найдено лиц: " << faces.size() << std::endl;
    return 0;
}
// Детекция объектов через предобученную нейросеть (DNN-модуль) — современный, более точный подход
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>

int main()
{
    // Загрузка предобученной модели (например, YOLO или SSD, в формате ONNX/TensorFlow)
    cv::dnn::Net net = cv::dnn::readNetFromONNX("object_detection_model.onnx");

    cv::Mat image = cv::imread("street_scene.jpg");

    cv::Mat blob;
    cv::dnn::blobFromImage(image, blob, 1.0 / 255.0, cv::Size(640, 640), cv::Scalar(), true, false);
    // blobFromImage — подготовка изображения в формат, ожидаемый нейросетью (нормализация, изменение размера)

    net.setInput(blob);
    cv::Mat output = net.forward(); // выполнение прямого прохода через нейросеть — РЕЗУЛЬТАТ детекции

    // Дальнейшая обработка output (специфична для конкретной архитектуры модели — YOLO, SSD, и т.д.)
    // включает фильтрацию по уверенности предсказания и подавление избыточных, перекрывающихся детекций (NMS)

    return 0;
}

Пояснения к коду

cv::CascadeClassifier::load() с готовым файлом haarcascade_frontalface_default.xml (поставляемым непосредственно с дистрибутивом OpenCV) показывает удобство классического подхода — для типичной, распространённой задачи детекции лиц не требуется ни сбора собственных данных, ни обучения собственной модели, достаточно использовать уже готовый, предобученный каскад. detectMultiScale(gray, faces, 1.1, 5) показывает типичные параметры этого метода — масштабный коэффициент (1.1, определяющий, насколько последовательно изменяется размер окна поиска при сканировании изображения на разных масштабах) и минимальное количество соседних обнаружений (5, для подавления ложных, единичных срабатываний), оба влияющих на компромисс между точностью обнаружения и скоростью работы. Пример с DNN-модулем показывает значительно более современный подход — readNetFromONNX загружает модель в широко распространённом, переносимом формате ONNX (Open Neural Network Exchange, поддерживающем экспорт моделей из множества различных фреймворков глубокого обучения), blobFromImage подготавливает входное изображение в конкретный числовой формат, ожидаемый загруженной нейросетью (нормализация значений пикселей, изменение размера до конкретного, фиксированного входного разрешения модели), и net.forward() выполняет реальное вычисление (прямой проход через все слои нейросети), результат которого затем требует дополнительной, специфичной для конкретной архитектуры модели обработки для извлечения итоговых, понятных результатов детекции (ограничивающих прямоугольников объектов и их классов).

Подводные камни

  • Использование Haar-каскадов для задач, существенно отличающихся от их специализированного назначения (детекция лиц/глаз в относительно типичных, фронтальных условиях) — Haar-каскады обучены для конкретной, узкой задачи и плохо обобщаются на сильно отличающиеся условия (значительные повороты лица, нетипичное освещение, объекты совершенно иной природы), и для более широкого спектра задач детекции (произвольные объекты, нестандартные условия съёмки) современный DNN-подход обычно даёт существенно лучшие, более устойчивые результаты, хотя и за счёт большей вычислительной стоимости.
  • Неверный выбор параметров detectMultiScale (масштабный коэффициент, минимальное количество соседей) без понимания их влияния на компромисс между скоростью и точностью — слишком «грубые» параметры (большой шаг масштаба, малое минимальное количество соседей) могут пропустить реальные объекты или выдать много ложных срабатываний, тогда как слишком «тонкие» параметры существенно увеличивают время обработки без соразмерного улучшения качества для многих практических случаев, и подбор подходящих параметров часто требует эмпирической настройки для конкретного типа обрабатываемых изображений.
  • Игнорирование необходимости постобработки результата DNN-модели (подавление немаксимумов, NMS — устранение множественных, избыточных, сильно перекрывающихся детекций одного и того же реального объекта; фильтрация по минимальному порогу уверенности предсказания) — необработанный, «сырой» выход большинства моделей детекции объектов содержит существенно больше потенциальных детекций, чем реальных объектов на изображении, и пропуск этой обязательной постобработки приведёт к множественным, дублирующим, частично пересекающимся прямоугольникам вокруг одного и того же реального объекта вместо ожидаемого единственного, корректного результата.
  • Несовместимость формата подготовки входных данных (blobFromImage параметры — нормализация, размер, порядок каналов) с тем, что реально ожидает конкретная загруженная модель — каждая нейросетевая архитектура и конкретный способ её обучения может предполагать специфичные, не универсальные параметры предобработки входного изображения (конкретное целевое разрешение, конкретный диапазон нормализации значений пикселей, порядок RGB или BGR), и использование параметров, не соответствующих тем, с которыми модель была реально обучена, приведёт к существенно ухудшенному, неверному результату детекции без какой-либо явной ошибки, лишь молчаливо некорректным результатом.