Введение
Как и большинства разработчиков Qt, использующих библиотеку OpenCV, меня заинтересовала тема представления изображения, полученного из web-камеры, как компонент визуального проектирования интерфейса для Qt Designer.
Перерыв кучу информации в сети, заметил, что большинство статей повторяют друг друга, и найти «изюминку» слишком трудно. Думаю, мой обыт создания визуальной компоненты по представлению изобравжения библиотеки OpenCV для Qt Designer будет полезен. К тому же, поделюсь информацией, как разделить библиотеки времени проектирования дизайна и времени выполнения кода. Подобный подход весьма удачно зарекомендовал себя в RAD Delphi для операционных систем семейства Windows.
Замечания
- Автор coffeesmoke не «разжёвывает» простые истины, методы и тонкости. Код понятен. Функции не превышают 20 строк.
- Проект и пример использования реализован на Qt Creator для ОС Linux. Разработчики Windows-приложений должны установить настройки в файлох .pro соответственно своей ОС
- Автор не пускается в дискуссии типа «а так было бы лучше». Нашли оптимальное решение, реализуйте.
- Автор не отвечает на вопросы настроек Qt Creator и/или Qt Designer. Поисковые системы вам в помощь!
- Пост несёт ознакомительный характер и служит обозначению направления движения ваших мыслей. Куда они пойдут, вам виднее.
Проектирование и выполнение
Выполнение
Оставим ненадолго библиотеку OpenCV работы с изображениями. Наша задача — универсальная runtime-библиотека (библиотека времени исполнения кода).
Возможно, то, что я сейчас покажу используется повсеместно. Однако, не встретил я на просторах сети по тематике построения плагинов под Qt Designer подобного подхода.
Задача состоит в том, чтобы не «тащить» библиотеку плагина Qt Designera в проект, а обойтись библиотекой времени исполнения. Очевидно, что runtime-библиотеку можно «обрамить» виджетом представления компонента на палитре Qt Dessigner
Дизайнер содержит простой компонент QLabel, способный передавать изображение свойством pixmap. Для наших целей этого достаточно. Обратимся к переводу документации Добавление модулей Qt Designer
и самому первоисточнику информации по ссылке Using Custom Widgets with Qt Designer.Самое полезное — информация о размещении пользовательских библиотек-плагинов! теперь нам известен путь назначения: $$QTDIR/plugin/designer
Оставим обвёртку нового компонента в стороне.Займёмся непосредственно компонентом runtime-библиотеки. Создадим динамически подгружаемую библиотеку с классом CQtOpenCVImage нашего нового виджета.
Заголовочный файл cqtopencvimage.h
#ifndef QTOPENCVIMAGE_H
#define QTOPENCVIMAGE_H
#include <QtDesigner/QDesignerExportWidget>
#include <QWidget>
#include <QUrl>
#include <QLabel>
#include "opencv2/opencv.hpp"
#include <QScopedPointer>
/*----------------------------------------------------------------------------*/
class CQtOpenCVImagePrivate;
/*----------------------------------------------------------------------------*/
class
#if defined(QDESIGNER_EXPORT_WIDGETS)
QDESIGNER_WIDGET_EXPORT
#else
Q_DECL_EXPORT
#endif
CQtOpenCVImage
: public QWidget
{
Q_OBJECT
Q_PROPERTY(QUrl capture READ getCapture WRITE slot_setCapture)
Q_PROPERTY(QString text READ getLabelText WRITE slot_setLabelText)
Q_PROPERTY(QPixmap pixmap READ getPixmap WRITE slot_setPixmap)
Q_PROPERTY(bool scaledContents READ hasScaledContents WRITE slot_setScaledContents)
Q_PROPERTY(bool grayscale READ isGrayscale WRITE slot_setGrayscale)
Q_PROPERTY(Qt::Alignment alignment READ getAlignment WRITE setAlignment)
public:
explicit
CQtOpenCVImage(QWidget* parent = 0, Qt::WindowFlags f = 0);
virtual
~CQtOpenCVImage();
QUrl& getCapture ();
QString getLabelText () const;
const
QPixmap* getPixmap () const;
const
QImage* getImage () const;
Qt::Alignment getAlignment() const;
void setAlignment(Qt::Alignment);
const
QLabel* getQLabel () const;
bool hasScaledContents() const;
bool isGrayscale() const;
public Q_SLOTS:
void slot_setCapture ( const QUrl& );
void slot_setLabelText ( const QString& );
void slot_setPixmap ( const QPixmap& );
void slot_setImage ( const QImage& );
void slot_setQLabel ( const QLabel& );
void slot_setGrayscale(bool);
void slot_setScaledContents(bool);
Q_SIGNALS:
void signal_Grayscale(bool);
void signal_ImageChanged();
void signal_CaptureChanged();
void signal_PixmapChanged();
void signal_LabelChanged();
void signal_AlignmentChanged();
private:
Q_DISABLE_COPY(CQtOpenCVImage)
Q_DECLARE_PRIVATE(CQtOpenCVImage)
QScopedPointer d_ptr;
};
/*----------------------------------------------------------------------------*/
#endif // QTOPENCVIMAGE_H
Обратите внимание на участки, выделенные специальным шрифтом
- Включение заголовочного файла QtDesigner/QDesignerExportWidget гарантирует подключение необходимого макроса QDESIGNER_WIDGET_EXPORT при использовании в файле проекта директивы
DEFINES += QDESIGNER_EXPORT_WIDGETS
- Класс CQtOpenCVImagePrivate скрывает внутренние переменные и указатели из объявления основного класса CQtOpenCVImage. Мне кажется это правильным: не следует перегружать объявления ненужной информацией;
- Объявление единственной приватной переменной-указателя
QScopedPointer<CQtOpenCVImagePrivate> d_ptr;
на структуру внутренней реализации алгоритмов и данных в рамках правил Qt. ИМХО, «хороший тон».
Наш класс CQtOpenCVImage предоставляет для Qt Designer свойства (Q_PROPERTY) визуального управления изображением. Методы записи свойст реализованы слотами void slot_**** (***).
И так, наследуемый от QWidget класс представления изображения, полученного из web(IP)-камеры при вызуальном проектировании даёт доступ к свойствам:
- capture — URL устройства видеозахвата или номер web-камеры;
- text — текстовая надпись. Аналог свойства text QLabel;
- pixmap — указатель на объект типа QPixmap. Аналог свойства pixmap QLabel;
- scaledContents — масштабируемость изображения. Аналог свойства scaledContents QLabel;
- grayscale — булевый переключатель цветового режима вывода изображения, где значение true соответствует градациям серого, а false — цветное (RGB);
- alignment — выравнивание содержимого text и pixmap. Аналог свойства alignment QLabel;
Думаю, назначения методов класса описывать не имеет смысла: название говорят сами за себя.
Перейдём к коду реализации классов (файл cqtopencvimage.cpp).
Рассмотрим скрытый класс CQtOpenCVImagePrivate, его атрибуты и методы.
class CQtOpenCVImagePrivate
{
Q_DECLARE_PUBLIC(CQtOpenCVImage)
public:
CQtOpenCVImagePrivate(CQtOpenCVImage* owner);
virtual
~CQtOpenCVImagePrivate();
CQtOpenCVImage* q_ptr; // указатель на экземпляр основного класса
QGridLayout* f_grid_layout; // сетка выравнивания находящихся в ней компонент по всему периметру
QLabel* f_label;// экземпляр типа QLabel для вывода изображения
QUrl f_capture_path;// URL устройства видеозахвата или его номер
QImage* p_qt_image; // Указател на экземпляр изображения типа QImage
CvCapture* p_capture; // Указател на экземпляр устройства видеозахвата в рамках описания OpenCV
IplImage* p_opencv_frame; // Указател на текущий фрем из p_capture
uint f_grayscaled:1; // признак представления изобравжения в градациях серого
void init (); // инициализация данных
void close (); // корекктное закрытие всех указателей
void free_qt_image (); // освобождение памяти экземпляра p_qt_image
void new_qt_image ();// распределение памяти для экземпляра p_qt_image
void free_capture (); // освобождение памяти экземпляра p_capture
void new_capture (); // распределение памяти для экземпляра p_capture
};
Перейдём к реализации методов.
/*----------------------------------------------------------------------------*/
void
CQtOpenCVImagePrivate::init ()
{
Q_ASSERT(q_ptr);
f_grid_layout = new QGridLayout(q_ptr);
Q_ASSERT(f_grid_layout);
f_label = new QLabel(q_ptr/*, Qt::WindowNoState*/);
Q_ASSERT(f_label);
f_grid_layout->addWidget (f_label);
p_qt_image = 0,
p_capture = 0,
p_opencv_frame = 0,
f_grayscaled = 0;
}
/*----------------------------------------------------------------------------*/
inline void
CQtOpenCVImagePrivate::close ()
{
free_qt_image ();
free_capture ();
}
/*----------------------------------------------------------------------------*/
CQtOpenCVImagePrivate::CQtOpenCVImagePrivate(CQtOpenCVImage* owner)
: q_ptr(owner)
{
init ();
}
/*----------------------------------------------------------------------------*/
CQtOpenCVImagePrivate::~CQtOpenCVImagePrivate ()
{
close ();
if(!(f_label->parent ()))
delete f_label;
if(!(f_grid_layout->parent ()))
delete f_grid_layout;
}
/*----------------------------------------------------------------------------*/
inline void
CQtOpenCVImagePrivate::free_qt_image ()
{
if(p_qt_image) {
delete p_qt_image;
p_qt_image = 0;
}
}
/*----------------------------------------------------------------------------*/
inline void
CQtOpenCVImagePrivate::free_capture ()
{
if(p_capture) {
cvReleaseCapture(&p_capture);
p_capture = 0;
}
}
Код понятен и читаем. Вопросов не должно возникнуть. Параметр CQtOpenCVImage* owner конструтора представляет указател на объект CQtOpenCVImage, содержащий экземпляр скрытого класса как переменную CQtOpenCVImage::d_ptr.
Как говорил доктор, герой Леонида Броневого, из фильма «Формула любви», — «Так, я продолжу...».
Рассмотрим определение устройства захвата методом new_capture
/*----------------------------------------------------------------------------*/
void
CQtOpenCVImagePrivate::new_capture ()
{
free_capture ();//освобождение устройства захвата
bool b_ok;
int i_index = f_capture_path.toString ().toInt (&b_ok); // определение номера камеры, если это номер, а не URL
/* информация к размышлению:
перезахват устройства, если камера реализована как CGI для вебсервера, возвращающий по одному кадру
if(b_ok) {
p_capture = cvCreateCameraCapture(i_index);
if(p_capture)
if(!p_opencv_frame)
p_opencv_frame = cvQueryFrame (p_capture);
} else {
while((p_capture =cvCaptureFromFile(f_capture_path.toString ().toStdString ().c_str ()))) {
p_opencv_frame = cvQueryFrame (p_capture);
new_qt_image ();
cvWaitKey (1000);
}
}
*/
// да, ну его, этот перезахват. Перезахватим по запросу методами OpenCV.
p_capture =
b_ok ?
cvCreateCameraCapture(i_index) :
cvCaptureFromFile(f_capture_path.toString ().toStdString ().c_str ());
p_opencv_frame = p_capture ? cvQueryFrame (p_capture) : 0; // получение фрейма методом из OpenCV
new_qt_image (); // формирование экземпляра QImage
}
Для подробного знакомства с OpenCV читайте Оглавление
Формирование изображения типа QImage:
/*----------------------------------------------------------------------------*/
void
CQtOpenCVImagePrivate::new_qt_image ()
{
if(!p_capture) return;
free_qt_image (); // освободим указатель на изображение
if(p_opencv_frame) {
// создадим OpenCV экземпляр изображения согласно параметру оттенка серого
IplImage *_tmp =
f_grayscaled ?
cvCreateImage(
cvSize( p_opencv_frame->width, p_opencv_frame->height ),
IPL_DEPTH_8U,
1
) :
cvCloneImage (p_opencv_frame)
;
try
{
// пребразование цвета в RGB или оттенки серого
cvCvtColor( p_opencv_frame, _tmp, f_grayscaled ? CV_RGB2GRAY : CV_BGR2RGB );
// формирование изображения типа QImage
p_qt_image = new QImage(
(const uchar*)(_tmp->imageData),
_tmp->width,
_tmp->height,
_tmp->widthStep,
(f_grayscaled ? QImage::Format_Indexed8 : QImage::Format_RGB888)
);
emit q_ptr->signal_ImageChanged (); // оповещение о готовности
q_ptr->slot_setPixmap (QPixmap::fromImage (*p_qt_image)); // отрисовать изображение на QLabel
} catch(...) {
// упс... Дрова -- в исходное, пельмени разлепить!
close ();
}
// не забываем прибрать за собой!
cvReleaseImage(&_tmp);
}
}
Думаю, с подводной частью айсберга всё понятно.
Реализация основного класса ещё проще. Даже, лень объяснять. Смотрите сами:
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
CQtOpenCVImage::CQtOpenCVImage(QWidget* parent, Qt::WindowFlags f)
: QWidget(parent, f),
d_ptr(new CQtOpenCVImagePrivate(this))
{
}
/*----------------------------------------------------------------------------*/
CQtOpenCVImage::~CQtOpenCVImage()
{
}
/*----------------------------------------------------------------------------*/
QUrl& CQtOpenCVImage::getCapture ()
{
return (d_func ()->f_capture_path);
}
/*----------------------------------------------------------------------------*/
QString CQtOpenCVImage::getLabelText () const
{
return (d_func ()->f_label->text ());
}
/*----------------------------------------------------------------------------*/
bool CQtOpenCVImage::hasScaledContents() const
{
return d_func ()->f_label->hasScaledContents ();
}
/*----------------------------------------------------------------------------*/
void CQtOpenCVImage::slot_setScaledContents(bool Value )
{
d_func ()->f_label->setScaledContents (Value);
}
/*----------------------------------------------------------------------------*/
bool CQtOpenCVImage::isGrayscale() const
{
return d_func ()->f_grayscaled;
}
/*----------------------------------------------------------------------------*/
void
CQtOpenCVImage::slot_setGrayscale( bool Value )
{
if(d_func ()->f_grayscaled != Value)
{
d_func ()->f_grayscaled = Value;
if(!(d_func ()->p_capture))
d_func ()->new_capture ();
else
d_func ()->new_qt_image ();
emit signal_Grayscale (d_func ()->f_grayscaled);
}
}
/*----------------------------------------------------------------------------*/
void CQtOpenCVImage::slot_setLabelText ( const QString& Value )
{
d_func ()->f_label->setText (Value);
}
/*----------------------------------------------------------------------------*/
void CQtOpenCVImage::slot_setCapture ( const QUrl& Value )
{
// Раскомментировать при необходимости исключения переинициализации захвата
// if(getCapture ().toString () != Value.toString () || !d_func ()->p_opencv_frame)
// {
d_func ()->f_capture_path = Value.toString ().trimmed ();
d_func ()->new_capture ();
emit signal_CaptureChanged ();
// }
}
/*----------------------------------------------------------------------------*/
const QPixmap*
CQtOpenCVImage::getPixmap () const
{
return ((const QPixmap*)(d_func ()->f_label->pixmap ()));
}
/*----------------------------------------------------------------------------*/
void
CQtOpenCVImage::slot_setPixmap ( const QPixmap& Value )
{
d_func ()->f_label->setPixmap (Value);
emit signal_PixmapChanged ();
}
/*----------------------------------------------------------------------------*/
const QImage*
CQtOpenCVImage::getImage () const
{
return(d_func ()->p_qt_image);
}
/*----------------------------------------------------------------------------*/
void
CQtOpenCVImage::slot_setImage ( const QImage& Value )
{
d_func ()->free_qt_image ();
d_func ()->p_qt_image = new QImage(Value);
slot_setPixmap (QPixmap::fromImage (*(d_func ()->p_qt_image)));
emit signal_ImageChanged ();
}
/*----------------------------------------------------------------------------*/
void
CQtOpenCVImage::slot_setQLabel ( const QLabel& Value)
{
d_func ()->f_label->setText (Value.text ());
emit signal_LabelChanged ();
}
/*----------------------------------------------------------------------------*/
Qt::Alignment
CQtOpenCVImage::getAlignment() const
{
return(d_func ()->f_label->alignment ());
}
/*----------------------------------------------------------------------------*/
void
CQtOpenCVImage::setAlignment(Qt::Alignment Value)
{
d_func ()->f_label->setAlignment (Value);
emit signal_AlignmentChanged ();
}
/*----------------------------------------------------------------------------*/
const QLabel*
CQtOpenCVImage::getQLabel () const
{
return ((const QLabel*)(d_func ()->f_label));
}
Формируем runtime-библиотеку. Опишем файл проекта images.pro:
TARGET = QtOpenCVImages
TARGET = $$qtLibraryTarget($$TARGET)
TEMPLATE = lib
CONFIG += debug_and_release
Не забываем включить:
DEFINES += QDESIGNER_EXPORT_WIDGETS
Определим путь, куда попадут файлы библиотек:
unix:!symbian {
target.path = $$PWD/../../../../lib
DESTDIR = $$PWD/../../../../lib
INSTALLS += target
}
Добавим файлы класса:
SOURCES +=
cqtopencvimage.cpp
HEADERS +=
cqtopencvimage.h
Определим пути заголовков файлов библиотек OpenCV и пути зависимостей проекта:
INCLUDEPATH += /usr/include/opencv2
DEPENDPATH += /usr/include/opencv2
Добавим библиотеку OpenCV (core и highgui) к нашему проекту:
#win32:CONFIG(release): LIBS += -L/usr/lib/ -lopencv_core
#else:win32:CONFIG(debug, debug|release): LIBS += -L/usr/lib/ -lopencv_cored
#else:
unix: LIBS += -L/usr/lib/ -lopencv_core
#win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../../../../../../usr/lib/release/ -lopencv_highgui
#else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../../../../usr/lib/debug/ -lopencv_highguid
#win32:CONFIG(release): LIBS += -L/usr/lib/release/ -lopencv_highgui
#else:symbian: LIBS += -lopencv_highgui
#else:
unix: LIBS += -L/usr/lib/ -lopencv_highgui
Уважаемые разработчики ОС Windows, я «соскочил» с иглы Microsoft давно и прошу прощения, что закомментировал пути библиотек для вашей ОС. Вы — народ неглупый, разберётесь.
*NIX-оиды, используйте команду ln -s, чтобы создать ссылки на ваши библиотеки из места расположения в каталог /usr/lib
Один раз создав ссылку, просто, пересобирайте проект, и всё будет работать нормально!
Итак, мы создали динамическую библиотеку Qt визуального компонента отображения рисунка, полученного из web-камеры средствами OpenCV.
Исходный код: Wu a La (в прямом смысле)
Проектирование
Настало время подключить нашу runtime-библиотеку к оболочке класса-плагина для Qt Designer.
Создадим проект библиотеки-плагина:
TARGET = QtOpenCVWidgets
TARGET = $$qtLibraryTarget($$TARGET)
TEMPLATE = lib
CONFIG += designer plugin release
DEFINES += QDESIGNER_EXPORT_WIDGETS
SOURCES +=
qtopencvimageplugin.cpp
../qtopencvwidgets.cpp
HEADERS +=
qtopencvimageplugin.h
../qtopencvwidgets.h
../runtime/images/cqtopencvimage.h
RESOURCES +=
qtopencvimages.qrc
unix:!symbian {
target.path = $$PWD/../../../lib
DESTDIR = $$PWD/../../../lib
INSTALLS += target
}
INCLUDEPATH += /usr/include/opencv2
DEPENDPATH += /usr/include/opencv2
#win32:CONFIG(release, debug|release): LIBS += -L/usr/lib/ -lQtOpenCVImages
#else:win32:CONFIG(debug, debug|release): LIBS += -L/usr/lib -lQtOpenCVImages
#else:symbian: LIBS += -lQtOpenCVImages
#else:
unix: LIBS += -L/usr/lib -lQtOpenCVImages
INCLUDEPATH += $$PWD/../runtime/images
DEPENDPATH += $$PWD/../runtime/images
Похож на проект Runtime, не так ли? Рассмотрм некоторые отличия:
- Изменилось название библиотеки: TARGET = QtOpenCVWidgets;
- Параметр CONFIG += designer plugin release содержит тэги plugin и designer;
- Плагин реализован в двух файлах по одному на заголовок и реализацию qtopencvimageplugin.* qtopencvwidgets.*<.i>;
Всё так же присутствует зависимость от заголовков библиотеки OpenCV т.к. добавилась зависимость от включённого заголовка cqtopencvimage.h нашего виджета QtOpenCVImages;
Включена динамическая runtime библиотека libQtOpenCVImages по символьной ссылке из/usr/lib/libQtOpenCVImages.so
, указывающей на её реальное расположение в каталоге проекта
Создаём виджет-плагин Qt Designer по всем канонам Qt: Creating Custom Widgets for Qt Designer (надо же,coffeesmoke, как громко сказано!):
qtopencvimageplugin.h
#ifndef QTOPENCVIMAGEPLUGIN_H
#define QTOPENCVIMAGEPLUGIN_H
#include <QObject>
#include <QDesignerCustomWidgetInterface>
class QtOpenCVImagePlugin : public QObject, public QDesignerCustomWidgetInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetInterface)
public:
explicit QtOpenCVImagePlugin(QObject *parent = 0);
QString name() const;
QString includeFile() const;
QString group() const;
QIcon icon() const;
QString toolTip() const;
QString whatsThis() const;
bool isContainer() const;
QWidget* createWidget(QWidget *parent);
void initialize(QDesignerFormEditorInterface *core);
bool isInitialized() const;
QString domXml() const;
signals:
public slots:
private:
bool f_init;
};
#endif // QTOPENCVIMAGEPLUGIN_H
Основная идея: объявить как Q_INTERFACE, наследуясь от QDesignerCustomWidgetInterface и перегрузить методы класса-интерфеса дизайнера согласно своим требованиям.
qtopencvimageplugin.cpp
#include "qtopencvimageplugin.h"
#include "cqtopencvimage.h"
QtOpenCVImagePlugin::QtOpenCVImagePlugin(QObject *parent) :
QObject(parent),
f_init(false)
{
}
QString
QtOpenCVImagePlugin::name() const
{
return "CQtOpenCVImage";// имя класса виджета
}
QString
QtOpenCVImagePlugin::includeFile() const
{
return QLatin1String("cqtopencvimage.h"); // название включения в файл ui_*.h новой формы
}
QString
QtOpenCVImagePlugin::group() const
{
return tr("OpenCV Widgets"); // название группы отображения на панели компонентов Qt Designer
}
QIcon
QtOpenCVImagePlugin::icon() const
{
return QIcon(":QtOpenCVLogo.png"); // значок виджета
}
QString
QtOpenCVImagePlugin::toolTip() const
{
return QString();
}
QString
QtOpenCVImagePlugin::whatsThis() const
{
return QString();
}
bool
QtOpenCVImagePlugin::isContainer() const
{
return false;
}
QWidget*
QtOpenCVImagePlugin::createWidget(QWidget *parent)
{
return new CQtOpenCVImage(parent); // вот она, реализация экземпляра класса нашего виджета!
}
void
QtOpenCVImagePlugin::initialize(QDesignerFormEditorInterface *core)
{
// установка признака инициализации при помещении на форму
if (f_init) return;
f_init = true;
}
bool
QtOpenCVImagePlugin::isInitialized() const
{
return f_init;
}
QString
QtOpenCVImagePlugin::domXml() const
{
// основные параметры для дизайнера
return "<ui language="c++">n"
" <widget class="CQtOpenCVImage" name="QtOpenCVImage">n"
" <property name="geometry">n"
" <rect>n"
" <x>0</x>n"
" <y>0</y>n"
" <width>400</width>n"
" <height>200</height>n"
" </rect>n"
" </property>n"
" </widget>n"
"</ui>";
}
Осталось совсем немного: создать общую обвёртку-контейнер для всех наших виджетов группы Qt <-> OpenCV
qtopencvwidgets.h
#ifndef QTOPENCVWIDGETS_H
#define QTOPENCVWIDGETS_H
#include <QObject>
#include <QtPlugin>
#include <QDesignerCustomWidgetCollectionInterface>
class QtOpenCVWidgets :
public QObject,
public QDesignerCustomWidgetCollectionInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetCollectionInterface)
public:
explicit QtOpenCVWidgets(QObject *parent = 0);
QList<QDesignerCustomWidgetInterface*> customWidgets() const { return f_plugins; }
private:
QList<QDesignerCustomWidgetInterface *> f_plugins;
};
#endif // QTOPENCVWIDGETS_H
qtopencvwidgets.cpp
#include "qtopencvwidgets.h"
#include "images/qtopencvimageplugin.h"
QtOpenCVWidgets::QtOpenCVWidgets(QObject *parent) :
QObject(parent)
{
f_plugins << new QtOpenCVImagePlugin(this);
}
//Q_DECLARE_INTERFACE(QtOpenCVWidgets, "com.trolltech.Qt.Designer.QtOpenCV")
Q_EXPORT_PLUGIN2(qtopencvwidgetsplugin, QtOpenCVWidgets)
Интерес представляет конструктор, а именно: f_plugins << new QtOpenCVImagePlugin(this);
. При последующем добавлении новых компонентов в коллекцию достаточно будет переписать конструктор, добавив ещё один оператор f_plugins << new <Очередной>Plugin(this);
Применение
Открываем Qt Designer и ищем на палитре компонентов наш СQtOpenCVImage. Пмещаем его на новую форму. Меняем capture на нужный URL http://192.168.0.20:8080/image.jpg
. В данном случае, это Java-аплет сервера камеры, предоставляющий текущий запрашиваемый кадр
Перейти к изображению
Поставим «галочку» на свойстве grayscale
Исходный код: Если скачали ранее, не переходите по ней.
Выводы
- Научились создавать библиотеку времени выполнения для реализации собственного класса на основе визуальных компонент Qt;
- Научились обрамлять runtime библиотеку в плагин Qt Designer, сделав приложения независимыми от плагина дизайнера на время выполнения;
- Прочли… документацию!
Приложения
Простой тестовый пример
Что за разработка без тестирования? Правильно, создадим тестовый пример.
Создадим стандартное Qt GUI приложение, откроем основную форму окна, поместим с неё наш компонент, создадим немного сервисных управлений, соберём и запустим.
Меняем «Адрес...» на значение 0 (встроенная видеокамера)
Цветной
Перейти к изображению
Оттенки серого
Исходный код: Если скачали ранее, не переходите по ней.
Рекомендации
Не заработало? Жаль… Но вы — гений, исправляйте!
Что дальше?
- Оптимизация текущего проекта;
- Описание проверенного визуального компонента поиска границ изображения методом Кэнни (Canny): CQtOpenCVCannyWidget на основе класса CQtOpenCVCanny;
- Связь между QtOpenCVImage и QtOpenCVCanny
До встречи!
Автор: coffeesmoke