В прошлый раз мы занимались установкой OpenCV в Linux. Теперь проделаем аналогичную процедуру по установке под Windows. А также создадим простое приложение, использующее модуль OpenCV для работы с веб-камерами:
Установка OpenCV под Windows
Подготовительные действия:
- Устанавливаем Git;
- Устанавливаем MinGW. Я установил его вместе с пакетом Qt;
- Устанавливаем Cmake;
- Прописываем пути к bin-каталогам MinGW и Cmake в переменную окружения PATH. По умолчанию Git сам прописывает пути к своим исполняемым файлам, поэтому дополнительные манипуляции с PATH для него не требуются.
Шаг 1: Получаем исходные коды OpenCV из Git
1 2 3 |
mkdir <путь_где_мы_хотим_вести_сборку> cd <путь_где_мы_хотим_вести_сборку> git clone https://github.com/opencv/opencv.git |
Шаг 2: Подготавливаем OpenCV к сборке
1 2 3 |
mkdir release # Предполагается, что мы в каталоге, куда был склонирован git-проект cd release cmake -G "MinGW Makefiles" -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=<путь_куда_мы_хотим_установить_opencv> -D CMAKE_CXX_COMPILER=g++.exe -D MAKE_MAKE_PROGRAM=mingw32-make.exe ..\ |
В качестве каталога установки я использую C:\OpenCV\.
Шаг 3: Собираем и устанавливаем OpenCV
1 2 |
mingw32-make # Предполагается, что мы в каталоге release/ mingw32-make install |
Если все прошло без ошибок, то мы можем проверить работоспособность сборки на примере по распознаванию лиц из прошлой статьи. Однако нам потребуется внести пару изменений в pro-файл. Они отражены в следующих строках:
1 2 3 4 5 6 7 8 9 10 11 |
# Не забываем указать действительные пути к заголовочным файлам и библиотекам INCLUDEPATH += C:/OpenCV/include/ LIBS += -LC:/OpenCV/x86/mingw/bin/ # Под Win32 имена библиотек получают суффикс с номером версии (например, libopencv_core320.dll) OPENCV_VER = 320 LIBS += -lopencv_core$${OPENCV_VER} \ -lopencv_imgproc$${OPENCV_VER} \ -lopencv_imgcodecs$${OPENCV_VER} \ -lopencv_highgui$${OPENCV_VER} \ -lopencv_objdetect$${OPENCV_VER} |
OpenCV: Реализация примера для работы с веб-камерой
Начинаем с pro-файла:
1 2 3 4 5 6 7 8 9 10 11 12 |
# ... # Остальное нас не интересует INCLUDEPATH += C:/OpenCV/include/ LIBS += -LC:/OpenCV/x86/mingw/bin/ win32: OPENCV_VER = 320 LIBS += -lopencv_core$${OPENCV_VER} \ -lopencv_imgproc$${OPENCV_VER} \ -lopencv_imgcodecs$${OPENCV_VER} \ -lopencv_highgui$${OPENCV_VER} \ -lopencv_videoio$${OPENCV_VER} |
По сравнению с примером чуть выше мы отключили модуль objdetect, но подключили videoio.
Заголовочный файл mainwidget.h:
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 |
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> #include <QRunnable> #include <QThreadPool> namespace Ui { class MainWidget; } class MainWidget : public QWidget { Q_OBJECT public: explicit MainWidget( QWidget* parent = 0 ); ~MainWidget(); signals: void stopCaptureSig(); private slots: void onFrameAvaliable( const QImage& img ); private: Ui::MainWidget* ui; QThreadPool m_pool; }; // ******************************************************************************** class WebCamStreamTask : public QObject, public QRunnable { Q_OBJECT public: explicit WebCamStreamTask( QObject* parent = 0 ); public slots: void onStop(); signals: void frameAvailable( const QImage& img ); protected: void run(); private: bool m_stopped; }; #endif // MAINWIDGET_H |
Обратите внимание, что мы заготовили класс задачи WebCamStreamTask. Именно здесь мы задействуем функции модуля VideoCapture. Главный поток управления с GUI будет всего лишь получать готовые изображения QImage и отображать их в QLabel.
Файл mainwidget.cpp:
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 |
#include "mainwidget.h" #include "ui_mainwidget.h" #include <opencv2/opencv.hpp> MainWidget::MainWidget( QWidget* parent ) : QWidget( parent ), ui( new Ui::MainWidget ) { ui->setupUi( this ); WebCamStreamTask* task = new WebCamStreamTask; connect( task, SIGNAL( frameAvailable( QImage ) ), SLOT( onFrameAvaliable( QImage ) ) ); connect( this, SIGNAL( stopCaptureSig() ), task, SLOT( onStop() ) ); m_pool.start( task ); } MainWidget::~MainWidget() { delete ui; emit stopCaptureSig(); m_pool.waitForDone(); } void MainWidget::onFrameAvaliable( const QImage& img ) { QPixmap pix = QPixmap::fromImage( img ).scaled( ui->lbPreview->size(), Qt::KeepAspectRatio ); ui->lbPreview->setPixmap( pix ); } // ******************************************************************************** WebCamStreamTask::WebCamStreamTask( QObject* parent ) : QObject( parent ), m_stopped( false ) { } void WebCamStreamTask::onStop() { m_stopped = true; } void WebCamStreamTask::run() { cv::VideoCapture cap; if( !cap.open( 0 ) ) { return; } forever { if( m_stopped ) { break; } cv::Mat frame; cap >> frame; if( frame.empty() ) { m_stopped = true; continue; } cv::Mat frameRGB; cv::cvtColor( frame, frameRGB, CV_BGR2RGB ); QImage img( reinterpret_cast< const uchar* >( frameRGB.data ), frameRGB.cols, frameRGB.rows, frameRGB.step, QImage::Format_RGB888 ); emit frameAvailable( img.copy() ); } } |
Особый интерес для нас представляет следующий фрагмент:
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 |
void WebCamStreamTask::run() { cv::VideoCapture cap; if( !cap.open( 0 ) ) { return; } forever { if( m_stopped ) { break; } cv::Mat frame; cap >> frame; if( frame.empty() ) { m_stopped = true; continue; } cv::Mat frameRGB; cv::cvtColor( frame, frameRGB, CV_BGR2RGB ); QImage img( reinterpret_cast< const uchar* >( frameRGB.data ), frameRGB.cols, frameRGB.rows, frameRGB.step, QImage::Format_RGB888 ); emit frameAvailable( img.copy() ); } } |
Сначала мы пытаемся открыть веб-камеру по умолчанию. Если что-то пошло не так, то поток прерывает свое выполнение (это не слишком информативно, поэтому никогда не делайте так в настоящем приложении):
1 2 3 4 |
cv::VideoCapture cap; if( !cap.open( 0 ) ) { return; } |
Далее мы запускаем бесконечный цикл (он остановится при выходе из приложения или при сбое в работе веб-камеры), в котором получаем кадры от веб-камеры:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
forever { if( m_stopped ) { break; } cv::Mat frame; cap >> frame; if( frame.empty() ) { m_stopped = true; continue; } // ... } |
Во второй части каждой итерации мы преобразуем полученный кадр (объект класса cv::Mat) в QImage, копию которого затем отправляем с помощью сигнала frameAvailable():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
forever { // ... cv::Mat frameRGB; cv::cvtColor( frame, frameRGB, CV_BGR2RGB ); QImage img( reinterpret_cast< const uchar* >( frameRGB.data ), frameRGB.cols, frameRGB.rows, frameRGB.step, QImage::Format_RGB888 ); emit frameAvailable( img.copy() ); } |
Следует заметить, что мы преобразуем исходный cv::Mat в определенный формат, из которого уже и формируется QImage. При этом мы отправляем именно копию QImage, потому что конструктор QImage не делает глубокую копию входного буфера с данными изображения.