IT Notes

OpenCV: Установка и использование под Windows

В прошлый раз мы познакомились с OpenCV под Linux. Теперь проделаем аналогичную процедуру по установке под Windows. А также создадим простое приложение, использующее модуль OpenCV для работы с веб-камерами:

opencv-web-cam-demo-app

Установка OpenCV под Windows

Подготовительные действия:

  1. Устанавливаем Git;
  2. Устанавливаем MinGW. Я установил его вместе с пакетом Qt;
  3. Устанавливаем Cmake;
  4. Прописываем пути к bin-каталогам MinGW и Cmake в переменную окружения PATH. По умолчанию Git сам прописывает пути к своим исполняемым файлам, поэтому дополнительные манипуляции с PATH для него не требуются.

Шаг 1: Получаем исходные коды OpenCV из Git

mkdir <путь_где_мы_хотим_вести_сборку>
cd <путь_где_мы_хотим_вести_сборку>
git clone https://github.com/opencv/opencv.git

Шаг 2: Подготавливаем OpenCV к сборке

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

mingw32-make # Предполагается, что мы в каталоге release/
mingw32-make install

Если все прошло без ошибок, то мы можем проверить работоспособность сборки на примере по распознаванию лиц из прошлой статьи ( скачать). Однако нам потребуется внести пару изменений в pro-файл. Они отражены в следующих строках:

# Не забываем указать действительные пути к заголовочным файлам и библиотекам
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-файла:

# …
# Остальное нас не интересует

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:

#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:

#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() );
    }
}

Особый интерес для нас представляет следующий фрагмент:

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() );
    }
}

Сначала мы пытаемся открыть веб-камеру по умолчанию. Если что-то пошло не так, то поток прерывает свое выполнение (это не слишком информативно, поэтому никогда не делайте так в настоящем приложении):

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) в QImage, копию которого затем отправляем с помощью сигнала frameAvailable():

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 не делает глубокую копию входного буфера с данными изображения.

Исходники

 Скачать пример использования OpenCV для захвата изображений с веб-камеры

Похожие публикации

Комментарии

супер, спасибо!

Спасибо за отзыв )

Если касаться темы OpenCV, могу подсказать идею маленькой, но интересной (я думаю многим начинающим программистам) программы, реализация которой может быть растянута на много статей. Бот, играющий в карточные игры. Например, собирающий "косынку". "Смотря" веб-камерой на монитор.Можно даже "клик" аппаратный сделать, на той же arduino (тема сама по себе заслуживает отдельной статьи). А если это будет еще и мультиплатформенно - будет вообще прекрасно. "… принесет тысячу лайков"

Да, понимаю сложность проекта, но вдруг…

Anonymous:

Если касаться темы OpenCV, могу подсказать идею маленькой, но интересной (я думаю многим начинающим программистам) программы, реализация которой может быть растянута на много статей. Бот, играющий в карточные игры. Например, собирающий "косынку". "Смотря" веб-камерой на монитор.Можно даже "клик" аппаратный сделать, на той же arduino (тема сама по себе заслуживает отдельной статьи). А если это будет еще и мультиплатформенно - будет вообще прекрасно. "… принесет тысячу лайков"

Да, понимаю сложность проекта, но вдруг…

Здравствуйте. Большое спасибо за идею. Звучит действительно очень интересно. Я еще подумаю, как можно оформить подобный материал, но думаю, что можно было бы этим заняться.

nextteh:

Подскажите этот пример работает только под Windows? У меня на Mac не работает, в чем может быть проблема? Хотя другие ваши примеры работают. Спасибо.

Здравствуйте. Формально пример и правда только для Windows, но это связано с pro-файлом. Должно быть достаточно поменять пути к библиотечным файлам. Т.е. сам код вполне кросс-платформенный.

Спасибо, весьма пригодилось!

С уважением, администратор сайта ***