В прошлый раз мы познакомились с OpenCV под Linux. Теперь проделаем аналогичную процедуру по установке под Windows. А также создадим простое приложение, использующее модуль OpenCV для работы с веб-камерами:
Подготовительные действия:
bin
-каталогам MinGW и Cmake в переменную окружения PATH
. По умолчанию Git сам прописывает пути к своим исполняемым файлам, поэтому дополнительные манипуляции с PATH
для него не требуются.mkdir <путь_где_мы_хотим_вести_сборку>
cd <путь_где_мы_хотим_вести_сборку>
git clone https://github.com/opencv/opencv.git
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\
.
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}
Начинаем с 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-файлом. Должно быть достаточно поменять пути к библиотечным файлам. Т.е. сам код вполне кросс-платформенный.
Спасибо, весьма пригодилось!
С уважением, администратор сайта ***
Anonymous
супер, спасибо!