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

Обзор OpenCV: архитектура библиотеки, модули, интеграция с CMake/Qt-проектом

Введение

После циклов про Qt Multimedia (статьи 467-478) и углублённого синтаксиса C/C++ переходим к новому, ранее не затронутому прикладному направлению — компьютерному зрению через OpenCV, одну из самых широко используемых библиотек обработки изображений и видео, естественно интегрируемую с Qt-приложениями. В этой обзорной статье разберём архитектуру библиотеки и базовую интеграцию через CMake (основной цикл статей).

Концепция

OpenCV организована как набор модулей, каждый отвечающий за определённую категорию функциональности — core (базовые структуры данных, статья 562), imgproc (обработка изображений — фильтры, преобразования, статьи 564-566), imgcodecs (загрузка/сохранение файлов изображений, статья 563), videoio (захват видео, статья 568), features2d (детекция признаков, статья 567), objdetect (детекция объектов, статья 569), и dnn (модуль глубокого обучения, статья 569) — это модульное разделение позволяет подключать только реально нужные модули, не раздувая итоговый размер скомпилированного приложения избыточными зависимостями. Интеграция с CMake-проектом (основной цикл статей про систему сборки) выполняется через find_package(OpenCV REQUIRED), после чего CMake автоматически находит установленную библиотеку и предоставляет переменные для подключения нужных заголовков и линковки соответствующих библиотек.

Пример кода: Обзор OpenCV

# CMakeLists.txt — интеграция OpenCV в Qt-проект
cmake_minimum_required(VERSION 3.16)
project(QtOpenCVApp)

find_package(Qt6 REQUIRED COMPONENTS Widgets)
find_package(OpenCV REQUIRED) # ищет установленную в системе библиотеку OpenCV

add_executable(QtOpenCVApp main.cpp)

target_link_libraries(QtOpenCVApp PRIVATE Qt6::Widgets ${OpenCV_LIBS})
target_include_directories(QtOpenCVApp PRIVATE ${OpenCV_INCLUDE_DIRS})
// Минимальная программа, подтверждающая корректную установку и подключение OpenCV
#include <opencv2/opencv.hpp> // общий заголовок, подключающий большинство базовых модулей
#include <iostream>

int main()
{
    std::cout << "Версия OpenCV: " << CV_VERSION << std::endl;

    cv::Mat testImage(100, 100, CV_8UC3, cv::Scalar(0, 255, 0)); // изображение 100x100, зелёного цвета
    std::cout << "Размер тестового изображения: " << testImage.size() << std::endl;

    return 0;
}
// Подключение только конкретных, нужных модулей вместо общего opencv.hpp
#include <opencv2/core.hpp>      // базовые структуры данных (статья 562)
#include <opencv2/imgcodecs.hpp> // загрузка/сохранение изображений (статья 563)
#include <opencv2/imgproc.hpp>   // обработка изображений (статьи 564-566)
// Более точечное подключение — может незначительно ускорить компиляцию для крупных проектов

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

find_package(OpenCV REQUIRED) в CMake показывает стандартный, идиоматичный способ интеграции внешней библиотеки (аналогично интеграции Qt в том же файле) — после успешного выполнения этой команды переменные OpenCV_LIBS/OpenCV_INCLUDE_DIRS автоматически содержат всё необходимое для подключения и линковки, без необходимости вручную прописывать пути к конкретным библиотечным файлам. Минимальная программа с созданием cv::Mat (статья 562) и выводом CV_VERSION показывает простейший способ подтвердить, что библиотека действительно корректно установлена и подключена к проекту, прежде чем переходить к более существенному, прикладному коду. Сравнение общего #include <opencv2/opencv.hpp> (подключающего практически все модули сразу) и точечного подключения отдельных заголовков конкретных модулей показывает практичный компромисс — для небольших, учебных программ общий заголовок удобен, но для крупных, активно компилируемых проектов точечное подключение только реально используемых модулей может дать заметную экономию времени компиляции, аналогично общему принципу минимизации избыточных include из основного цикла статей.

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

  • Установка OpenCV без явного понимания, какие именно модули реально нужны для конкретного проекта, особенно для embedded-целей (статьи 351-360) с ограниченным объёмом доступного хранилища — OpenCV в полной сборке со всеми модулями (включая dnn, статья 569, требующий значительных дополнительных зависимостей) может быть существенно больше, чем минимальная сборка только с реально нужными модулями, и для embedded/мобильных целей стоит явно настроить сборку OpenCV с ограниченным набором модулей через соответствующие CMake-опции самой OpenCV при её собственной сборке из исходников.
  • Несовместимость версии OpenCV, установленной в системе, с версией, для которой написан используемый код/документация — API OpenCV эволюционировал между крупными версиями (особенно переход от OpenCV 2.x к 3.x/4.x), и код, написанный для одной версии, не всегда напрямую компилируется с другой версии без модификации, и явная проверка версии (CV_VERSION, как показано в примере) и сопоставление с используемой документацией/примерами — важный шаг при начале работы с конкретной, установленной в системе версией.
  • Игнорирование того, что для большинства desktop-дистрибутивов OpenCV не входит по умолчанию в систему, требуя отдельной, явной установки (через пакетный менеджер системы, vcpkg/conan, основной цикл статей про управление зависимостями, или сборку из исходников) — в отличие от Qt, для которого основной цикл статей подробно разбирал процесс установки, OpenCV требует аналогичного, отдельного внимания к процессу установки и интеграции, который варьируется в зависимости от конкретной целевой платформы и выбранного способа установки.
  • Смешивание заголовков и библиотек OpenCV разных версий, установленных одновременно в одной системе (например, через системный пакетный менеджер и отдельно через vcpkg) — это может привести к неочевидным ошибкам компиляции или линковки, когда заголовочные файлы одной версии используются вместе с библиотечными файлами другой, несовместимой версии, и явная, согласованная настройка путей поиска (через CMake) для использования одной, конкретной, целостной установки OpenCV важна для избежания таких трудно диагностируемых проблем.