📜 ⬆️ ⬇️

Qt Designer & Runtime Qt library on the service of OpenCV developer, or dragging IplImage on QLabel

Introduction


Like most Qt developers using the OpenCV library, I was interested in the topic of presenting an image obtained from a webcam as a component of the visual interface design for Qt Designer.
Breaking a bunch of information online, noticed that most articles are repeating each other, and finding the "highlight" is too difficult. I think my experience of creating a visual component on the representation of the OpenCV library image for Qt Designer will be useful. In addition, I will share information on how to separate libraries of design design time and code execution time. This approach has very successfully established itself in RAD Delphi for Windows operating systems.

Remarks



Design and Execution


Performance

Let's leave for a while the OpenCV image library. Our task is a universal runtime library (code execution time library).
Perhaps what I show now is used everywhere. However, I did not meet on the network open spaces on the subject of building plugins for Qt Designer of a similar approach.
The task is not to “drag” the Qt Designera plugin library into the project, but to get along with the runtime library. Obviously, the runtime library can be “framed” by the component's widget in the Qt Dessigner palette.

The designer contains a simple QLabel component that can transmit an image with the pixmap property. For our purposes, this is enough. Referring to the translation of documentation Adding Qt Designer Modules
and the most original source of information on the link Using Custom Widgets with Qt Designer. The most useful is information about the placement of custom plug-in libraries! now we know the destination path: $$ QTDIR / plugin / designer
Let's leave the wrapper of the new component to the side. Let's take a component of the runtime library directly. Let's create a dynamically loaded library with the CQtOpenCVImage class of our new widget.
Cqtopencvimage.h header file
')
#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 


Pay attention to the areas marked with a special font.

Our class CQtOpenCVImage provides Qt Designer properties (Q_PROPERTY) for visual image control. Property recording methods are implemented with void slot _ **** (***) slots.
And so, the class of representation of an image derived from a web (IP) camera inherited from QWidget during custom design gives access to the properties:


I think the purpose of class methods to describe does not make sense: the name speaks for itself.

Let us turn to the code for the implementation of classes (file cqtopencvimage.cpp ).

Consider the hidden class CQtOpenCVImagePrivate , its attributes and methods.
 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 }; 

Let us turn to the implementation of methods.
 /*----------------------------------------------------------------------------*/ 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; } } 

The code is clear and readable. Questions should not arise. The CQtOpenCVImage * owner parameter of the constructor represents a pointer to a CQtOpenCVImage object containing an instance of the hidden class as the variable CQtOpenCVImage :: d_ptr.
As the doctor said, the hero Leonid Bronevoy, from the film “Formula of Love”, - “So, I will continue ...”.

Consider the definition of a capture device using the new_capture method .
 /*----------------------------------------------------------------------------*/ 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 } 

For a detailed introduction to OpenCV, read the Table of Contents.

Image formation type 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); } } 



I think everything is clear with the underwater part of the iceberg.

Implementing the main class is even easier. Even too lazy to explain. See for yourself:
 /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ 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)); } 


We form runtime-library. We describe the project file images.pro:
 TARGET = QtOpenCVImages TARGET = $$qtLibraryTarget($$TARGET) TEMPLATE = lib CONFIG += debug_and_release 

Do not forget to include:
 DEFINES += QDESIGNER_EXPORT_WIDGETS 

Determine the path to get the library files:
 unix:!symbian { target.path = $$PWD/../../../../lib DESTDIR = $$PWD/../../../../lib INSTALLS += target } 


Add class files:
 SOURCES += \ cqtopencvimage.cpp HEADERS += \ cqtopencvimage.h 


Define the paths of the OpenCV library file headers and project dependency paths:
 INCLUDEPATH += /usr/include/opencv2 DEPENDPATH += /usr/include/opencv2 


Add the OpenCV library (core and highgui) to our project:
 #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 


Dear developers of the Windows OS, I “jumped” off the Microsoft needle for a long time and I apologize for commenting out the library paths for your OS. You are smart people, you will understand.
* NIX, use the ln -s command to create links to your libraries from the location in the /usr/lib directory
Once you create a link, just rebuild the project, and everything will work fine!

So, we have created a Qt dynamic library of a visual component for displaying a picture obtained from a webcam using OpenCV.

Source Code: Wu a La (literally)

Design

It is time to connect our runtime library to the shell class of the plugin for Qt Designer.
Create a plugin library project:
 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 

Looks like a runtime project, right? Consider some of the differences:

Create a Qt Designer plugin widget for all Qt canons: Creating Custom Widgets for Qt Designer ( coffeesmoke , as loudly said!):
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 

The basic idea is to declare Q_INTERFACE, inheriting from QDesignerCustomWidgetInterface, and reload the methods of the designer’s interface class in accordance with its requirements.

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>"; } 



Very little remains: create a common wrapper container for all our Qt <-> OpenCV group widgets
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); interest is the constructor, namely: f_plugins << new QtOpenCVImagePlugin(this); . At the subsequent addition of new components to the collection, it will be enough to rewrite the constructor, adding one more f_plugins operator << new <Next> Plugin (this);
Application

Open Qt Designer and look at our QtOpenCVImage on the component palette. Move it to a new form. Change the capture to the desired URL 192.168.0.20:8080/image.jpg 192.168.0.20:8080/image.jpg . In this case, this is a Java server applet of the camera server providing the current requested frame.
Put a "tick" on the property grayscale
Source code: If downloaded earlier, do not go on it .

findings



Applications


Simple test case

Create a standard Qt GUI application, open the main form of the window, place our component with it, create some service controls, build and run.
Change the "Address ..." to 0 (built-in video camera) and look at the image changes.

Source code: If downloaded earlier, do not go on it .

What's next?



All the best.

Source: https://habr.com/ru/post/146210/


All Articles