《OpenCV3和Qt5计算机视觉应用开发》学习笔记第6章

本章将介绍以下主题:
如何为Computer_Vision工程项目创建一个新插件并学习每个OpenCV技术
如何对图像进行滤波
如何进行图像变换
对于颜色空间,如何进行颜色空间之间的相互转换,以及如何应用颜色映射
图像阈值化
OpenCV中可用的绘图函数
模板匹配以及如何使用模板匹配进行物体检测与计数

首先看工程结果,这个工程由以下模块组成,mainapp是主程序,其他都是插件程序。

主项目computer_vision.pro

TEMPLATE = subdirs
SUBDIRS += \
    mainapp \
    template_plugin \
    copymakeborder_plugin \
    filter_plugin \
    transform_plugin \
    color_plugin \
    segmentation_plugin \
    fourier_plugin #傅里叶变换

模板插件template_plugin.pro


QT       += widgets

TARGET = Template_Plugin
TEMPLATE = lib
#模板插件
CONFIG += plugin

DEFINES += TEMPLATE_PLUGIN_LIBRARY

# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

#win环境配置
win32:{
    CONFIG(debug, debug|release){
        #插件生成目录
        DESTDIR += $$PWD/../../bin/win64d/cvplugins
        LIBS += -L$$PWD/../../lib/win64d -lopencv_world480d
    }else{
        DESTDIR += $$PWD/../../bin/win64/cvplugins
        LIBS += -L$$PWD/../../lib/win64 -lopencv_world480
    }
}

INCLUDEPATH += ../../Include


INCLUDEPATH += ../cvplugininterface

SOURCES += \
        template_plugin.cpp

HEADERS += \
        template_plugin.h \
        template_plugin_global.h

unix {
    target.path = /usr/lib
    INSTALLS += target
}

win32: {
    include("c:/dev/opencv/opencv.pri")
}

unix: !macx{
    CONFIG += link_pkgconfig
    PKGCONFIG += opencv
}

unix: macx{
INCLUDEPATH += "/usr/local/include"
LIBS += -L"/usr/local/lib" \
    -lopencv_world
}

FORMS += \
    plugin.ui

template_plugin_global.h

#ifndef TEMPLATE_PLUGIN_GLOBAL_H
#define TEMPLATE_PLUGIN_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(TEMPLATE_PLUGIN_LIBRARY)
#  define TEMPLATE_PLUGINSHARED_EXPORT Q_DECL_EXPORT
#else
#  define TEMPLATE_PLUGINSHARED_EXPORT Q_DECL_IMPORT
#endif

#endif // TEMPLATE_PLUGIN_GLOBAL_H

template_plugin.h

#ifndef TEMPLATE_PLUGIN_H
#define TEMPLATE_PLUGIN_H

#include "template_plugin_global.h"
#include "cvplugininterface.h"

namespace Ui {
    class PluginGui;
}

//继承CvPluginInterface类,重写title(), version(), description(), help(), setupUi(QWidget *parent), processImage函数
class TEMPLATE_PLUGINSHARED_EXPORT Template_Plugin: public QObject, public CvPluginInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "com.computervision.cvplugininterface")
    Q_INTERFACES(CvPluginInterface)
public:
    Template_Plugin();
    ~Template_Plugin();

    QString title();
    QString version();
    QString description();
    QString help();
    void setupUi(QWidget *parent);
    void processImage(const cv::Mat &inputImage, cv::Mat &outputImage);

signals:
    void updateNeeded();
    void errorMessage(QString msg);
    void infoMessage(QString msg);

private:
    Ui::PluginGui *ui;

};

#endif // TEMPLATE_PLUGIN_H

template_plugin.cpp

#include "template_plugin.h"

#include "ui_plugin.h"

Template_Plugin::Template_Plugin()
{
    // Insert initialization codes here ...
}

Template_Plugin::~Template_Plugin()
{
    // Insert cleanup codes here ...
}

QString Template_Plugin::title()
{
    return this->metaObject()->className();
}

QString Template_Plugin::version()
{
    return "1.0.0";
}

QString Template_Plugin::description()
{
    return "A <b>Template</b> plugin";
}

QString Template_Plugin::help()
{
    return "This is a <b>Template</b> plugin. Clone and use it to create new plugins.";
}

void Template_Plugin::setupUi(QWidget *parent)
{
    ui = new Ui::PluginGui;
    ui->setupUi(parent);

    // Connect signals for GUI elemnts manually here since they won't be connected by name in a plugin
    // ...
    // emit updateNeeded(); should be added whenever parameters on the plugin GUI change
}

void Template_Plugin::processImage(const cv::Mat &inputImage, cv::Mat &outputImage)
{
    // Replace the following line with the actual image processing task
    inputImage.copyTo(outputImage);

    // Otherwise, if the process doesn't affect the output image, update plugin GUI here ...
}

边框插件算法代码,在图像的周围形成边框

copymakeborder_plugin.h

#ifndef COPYMAKEBORDER_PLUGIN_H
#define COPYMAKEBORDER_PLUGIN_H

#include "copymakeborder_plugin_global.h"
#include "cvplugininterface.h"

namespace Ui {
    class PluginGui;
}

class COPYMAKEBORDER_PLUGINSHARED_EXPORT CopyMakeBorder_Plugin: public QObject, public CvPluginInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "com.computervision.cvplugininterface")
    Q_INTERFACES(CvPluginInterface)
public:
    CopyMakeBorder_Plugin();
    ~CopyMakeBorder_Plugin();

    QString title();
    QString version();
    QString description();
    QString help();
    void setupUi(QWidget *parent);
    void processImage(const cv::Mat &inputImage, cv::Mat &outputImage);

signals:
    void updateNeeded();
    void errorMessage(QString msg);
    void infoMessage(QString msg);

private slots:
    void on_borderTypeComboBox_currentIndexChanged(int index);

private:
    Ui::PluginGui *ui;

};

#endif // COPYMAKEBORDER_PLUGIN_H
copymakeborder_plugin.cpp
#include "copymakeborder_plugin.h"

#include "ui_plugin.h"

CopyMakeBorder_Plugin::CopyMakeBorder_Plugin()
{
    // Insert initialization codes here ...
}

CopyMakeBorder_Plugin::~CopyMakeBorder_Plugin()
{
    // Insert cleanup codes here ...
}

QString CopyMakeBorder_Plugin::title()
{
    return this->metaObject()->className();
}

QString CopyMakeBorder_Plugin::version()
{
    return "1.0.0";
}

QString CopyMakeBorder_Plugin::description()
{
    return "";
}

QString CopyMakeBorder_Plugin::help()
{
    return "";
}

void CopyMakeBorder_Plugin::setupUi(QWidget *parent)
{
    ui = new Ui::PluginGui;
    ui->setupUi(parent);
    QStringList items;   //边框类型
    items.append("BORDER_CONSTANT");
    items.append("BORDER_REPLICATE");
    items.append("BORDER_REFLECT");
    items.append("BORDER_WRAP");
    items.append("BORDER_REFLECT_101");
    ui->borderTypeComboBox->addItems(items);
    connect(ui->borderTypeComboBox, SIGNAL(currentIndexChanged(int)),
            this, SLOT(on_borderTypeComboBox_currentIndexChanged(int)));
}

void CopyMakeBorder_Plugin::processImage(const cv::Mat &inputImage, cv::Mat &outputImage)
{
    int top, bot, left, right;
    top = bot = inputImage.rows/2;
    left = right = inputImage.cols/2;
    //图像周围形成边框
    cv::copyMakeBorder(inputImage, outputImage, top, bot, left, right, ui->borderTypeComboBox->currentIndex());
}

void CopyMakeBorder_Plugin::on_borderTypeComboBox_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    emit updateNeeded();
}
滤波插件代码filter_plugin.h
#ifndef FILTER_PLUGIN_H
#define FILTER_PLUGIN_H

#include "filter_plugin_global.h"
#include "cvplugininterface.h"
#include <QObject>
#include <QSpinBox>

namespace Ui {
    class PluginGui;
}

class FILTER_PLUGINSHARED_EXPORT Filter_Plugin: public QObject, public CvPluginInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "com.computervision.cvplugininterface")
    Q_INTERFACES(CvPluginInterface)
public:
    Filter_Plugin();
    ~Filter_Plugin();

    QString title();
    QString version();
    QString description();
    QString help();
    void setupUi(QWidget *parent);
    void processImage(const cv::Mat &inputImage, cv::Mat &outputImage);

signals:
    void updateNeeded();
    void errorMessage(QString msg);
    void infoMessage(QString msg);

private slots:
    void on_bilateralDiaSpin_valueChanged(int arg1);

    void on_bilateralSigmaColorSpin_valueChanged(double arg1);

    void on_bilateralSigmaSpaceSpin_valueChanged(double arg1);

    void on_blurKernelSizeSpinBox_valueChanged(int arg1);

    void on_blurAnchoXSpin_valueChanged(int arg1);

    void on_blurAnchoYSpin_valueChanged(int arg1);

    void on_boxKernelSizeSpinBox_valueChanged(int arg1);

    void on_boxDepthSpin_valueChanged(int arg1);

    void on_boxAnchoXSpin_valueChanged(int arg1);

    void on_boxAnchoYSpin_valueChanged(int arg1);

    void on_boxNormalCheck_toggled(bool checked);

    void on_gaussKernelSizeSpinBox_valueChanged(int arg1);

    void on_gaussSigmaXSpin_valueChanged(double arg1);

    void on_gaussSigmaYSpin_valueChanged(double arg1);

    void on_medianApertureSpin_valueChanged(int arg1);

    void on_mainTabs_currentChanged(int index);

    void on_derivSobelRadio_toggled(bool checked);

    void on_derivScharrRadio_toggled(bool checked);

    void on_derivLaplacRadio_toggled(bool checked);

    void on_derivScaleSpin_valueChanged(double arg1);

    void on_derivDeltaSpin_valueChanged(double arg1);

    void on_morphErodeRadio_toggled(bool checked);

    void on_morphDilateRadio_toggled(bool checked);

    void on_morphMorphRadio_toggled(bool checked);

    void on_morphIterSpin_valueChanged(int arg1);

    void on_morphTypesCombo_currentIndexChanged(int index);

    void on_morphShapesCombo_currentIndexChanged(int index);

private:
    Ui::PluginGui *ui;

};

#endif // FILTER_PLUGIN_H
filter_plugin.cpp 
#include "filter_plugin.h"

#include "ui_plugin.h"

#define BILATERAL_FILTER_PAGE           0
#define BLUR_FILTER_PAGE                1
#define BOX_FILTER_PAGE                 2
#define GAUSSIAN_FILTER_PAGE            3
#define MEDIAN_FILTER_PAGE              4
#define FILTER2D_PAGE                   5
#define DERIVATIVES_PAGE                6
#define MORPH_PAGE                      7

Filter_Plugin::Filter_Plugin()
{
    // Insert initialization codes here ...
}

Filter_Plugin::~Filter_Plugin()
{
    // Insert cleanup codes here ...
}

QString Filter_Plugin::title()
{
    return this->metaObject()->className();
}

QString Filter_Plugin::version()
{
    return "1.0.0";
}

QString Filter_Plugin::description()
{
    return "Performs different filters available in OpenCV";
}

QString Filter_Plugin::help()
{
    return "This is a plugin that performs different filters available in OpenCV";
}

void Filter_Plugin::setupUi(QWidget *parent)
{
    ui = new Ui::PluginGui;
    ui->setupUi(parent);

    ui->mainTabs->setCurrentIndex(0);

    connect(ui->mainTabs, SIGNAL(currentChanged(int)), this, SLOT(on_mainTabs_currentChanged(int)));

    connect(ui->bilateralDiaSpin, SIGNAL(valueChanged(int)), this, SLOT(on_bilateralDiaSpin_valueChanged(int)));
    connect(ui->bilateralSigmaColorSpin, SIGNAL(valueChanged(double)), this, SLOT(on_bilateralSigmaColorSpin_valueChanged(double)));
    connect(ui->bilateralSigmaSpaceSpin, SIGNAL(valueChanged(double)), this, SLOT(on_bilateralSigmaSpaceSpin_valueChanged(double)));

    connect(ui->blurKernelSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(on_blurKernelSizeSpinBox_valueChanged(int)));
    connect(ui->blurAnchoXSpin, SIGNAL(valueChanged(int)), this, SLOT(on_blurAnchoXSpin_valueChanged(int)));
    connect(ui->blurAnchoYSpin, SIGNAL(valueChanged(int)), this, SLOT(on_blurAnchoYSpin_valueChanged(int)));

    connect(ui->boxKernelSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(on_boxKernelSizeSpinBox_valueChanged(int)));
    connect(ui->boxDepthSpin, SIGNAL(valueChanged(int)), this, SLOT(on_boxDepthSpin_valueChanged(int)));
    connect(ui->boxAnchoXSpin, SIGNAL(valueChanged(int)), this, SLOT(on_boxAnchoXSpin_valueChanged(int)));
    connect(ui->boxAnchoYSpin, SIGNAL(valueChanged(int)), this, SLOT(on_boxAnchoYSpin_valueChanged(int)));
    connect(ui->boxNormalCheck, SIGNAL(toggled(bool)), this, SLOT(on_boxNormalCheck_toggled(bool)));

    connect(ui->gaussKernelSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(on_gaussKernelSizeSpinBox_valueChanged(int)));
    connect(ui->gaussSigmaXSpin, SIGNAL(valueChanged(double)), this, SLOT(on_gaussSigmaXSpin_valueChanged(double)));
    connect(ui->gaussSigmaYSpin, SIGNAL(valueChanged(double)), this, SLOT(on_gaussSigmaYSpin_valueChanged(double)));

    connect(ui->medianApertureSpin, SIGNAL(valueChanged(int)), this, SLOT(on_medianApertureSpin_valueChanged(int)));

    connect(ui->derivSobelRadio, SIGNAL(toggled(bool)), this, SLOT(on_derivSobelRadio_toggled(bool)));
    connect(ui->derivScharrRadio, SIGNAL(toggled(bool)), this, SLOT(on_derivScharrRadio_toggled(bool)));
    connect(ui->derivLaplacRadio, SIGNAL(toggled(bool)), this, SLOT(on_derivLaplacRadio_toggled(bool)));
    connect(ui->derivDeltaSpin, SIGNAL(valueChanged(double)), this, SLOT(on_derivDeltaSpin_valueChanged(double)));
    connect(ui->derivScaleSpin, SIGNAL(valueChanged(double)), this, SLOT(on_derivScaleSpin_valueChanged(double)));

    ui->morphShapesCombo->addItems(
                QStringList() << "MORPH_RECT" << "MORPH_CROSS" << "MORPH_ELLIPSE");
    ui->morphTypesCombo->addItems(
                QStringList() << "MORPH_ERODE" << "MORPH_DILATE" << "MORPH_OPEN" << "MORPH_CLOSE"
                << "MORPH_GRADIENT" << "MORPH_TOPHAT" << "MORPH_BLACKHAT");
    connect(ui->morphDilateRadio, SIGNAL(toggled(bool)), this, SLOT(on_morphDilateRadio_toggled(bool)));
    connect(ui->morphErodeRadio, SIGNAL(toggled(bool)), this, SLOT(on_morphErodeRadio_toggled(bool)));
    connect(ui->morphMorphRadio, SIGNAL(toggled(bool)), this, SLOT(on_morphMorphRadio_toggled(bool)));
    connect(ui->morphIterSpin, SIGNAL(valueChanged(int)), this, SLOT(on_morphIterSpin_valueChanged(int)));
    connect(ui->morphShapesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(on_morphShapesCombo_currentIndexChanged(int)));
    connect(ui->morphTypesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(on_morphTypesCombo_currentIndexChanged(int)));
}

void Filter_Plugin::processImage(const cv::Mat &inputImage, cv::Mat &outputImage)
{
    using namespace cv;
    Matx33f f2dkernel;

    switch(ui->mainTabs->currentIndex())
    {

    case BILATERAL_FILTER_PAGE:
        bilateralFilter(inputImage,
                        outputImage,
                        ui->bilateralDiaSpin->value(),
                        ui->bilateralSigmaColorSpin->value(),
                        ui->bilateralSigmaSpaceSpin->value());
        break;

    case BLUR_FILTER_PAGE:
        blur(inputImage,
             outputImage,
             Size(ui->blurKernelSizeSpinBox->value(),
                  ui->blurKernelSizeSpinBox->value()),
             Point(ui->blurAnchoXSpin->value(),
                   ui->blurAnchoYSpin->value()));
        break;

    case BOX_FILTER_PAGE:
        boxFilter(inputImage,
                  outputImage,
                  ui->boxDepthSpin->value(),
                  Size(ui->boxKernelSizeSpinBox->value(),
                       ui->boxKernelSizeSpinBox->value()),
                  Point(ui->boxAnchoXSpin->value(),
                        ui->boxAnchoYSpin->value()),
                  ui->boxNormalCheck->isChecked());

        // replace with sqrBoxFilter
        break;

    case GAUSSIAN_FILTER_PAGE:
        GaussianBlur(inputImage,
                     outputImage,
                     Size(ui->gaussKernelSizeSpinBox->value(),
                          ui->gaussKernelSizeSpinBox->value()),
                     ui->gaussSigmaXSpin->value(),
                     ui->gaussSigmaYSpin->value());
        break;

    case MEDIAN_FILTER_PAGE:
        medianBlur(inputImage,
                   outputImage,
                   ui->medianApertureSpin->value());
        break;

    case FILTER2D_PAGE:
        f2dkernel = Matx33f(0, +1.5, 0,
                            +1.5, -6, +1.5,
                            0, +1.5, 0);

        filter2D(inputImage,
                 outputImage,
                 -1, // Output should have same depth as source
                 f2dkernel,
                 Point(-1,-1));
        break;

    case DERIVATIVES_PAGE:
        if(ui->derivSobelRadio->isChecked())
            Sobel(inputImage, outputImage, -1, 1, 1, 3, ui->derivScaleSpin->value(), ui->derivDeltaSpin->value());
        else if(ui->derivScharrRadio->isChecked())
            Scharr(inputImage, outputImage, -1, 1, 0, ui->derivScaleSpin->value(), ui->derivDeltaSpin->value());
        else if(ui->derivLaplacRadio->isChecked())
            Laplacian(inputImage, outputImage, -1, 3, ui->derivScaleSpin->value(), ui->derivDeltaSpin->value());
        break;

    case MORPH_PAGE:
        if(ui->morphErodeRadio->isChecked())
        {
            erode(inputImage,
                  outputImage,
                  getStructuringElement(ui->morphShapesCombo->currentIndex(),
                                        Size(5,5)),
                  Point(-1,-1),
                  ui->morphIterSpin->value());
        }
        else if(ui->morphDilateRadio->isChecked())
        {
            dilate(inputImage,
                   outputImage,
                   getStructuringElement(ui->morphShapesCombo->currentIndex(),
                                         Size(5,5)),
                   Point(-1,-1),
                   ui->morphIterSpin->value());
        }
        else if(ui->morphMorphRadio->isChecked())
        {
            morphologyEx(inputImage,
                         outputImage,
                         ui->morphTypesCombo->currentIndex(),
                         getStructuringElement(ui->morphShapesCombo->currentIndex(),
                                               Size(5,5)),
                         Point(-1,-1),
                         ui->morphIterSpin->value());
        }
        break;

    }
}

void Filter_Plugin::on_bilateralDiaSpin_valueChanged(int arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_bilateralSigmaColorSpin_valueChanged(double arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_bilateralSigmaSpaceSpin_valueChanged(double arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_blurKernelSizeSpinBox_valueChanged(int arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_blurAnchoXSpin_valueChanged(int arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_blurAnchoYSpin_valueChanged(int arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_boxKernelSizeSpinBox_valueChanged(int arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_boxDepthSpin_valueChanged(int arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_boxAnchoXSpin_valueChanged(int arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_boxAnchoYSpin_valueChanged(int arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_boxNormalCheck_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Filter_Plugin::on_gaussKernelSizeSpinBox_valueChanged(int arg1)
{
    if(arg1 % 2 == 1) // Must be odd
        emit updateNeeded();
}

void Filter_Plugin::on_gaussSigmaXSpin_valueChanged(double arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_gaussSigmaYSpin_valueChanged(double arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_medianApertureSpin_valueChanged(int arg1)
{
    if(arg1 % 2 == 1) // Must be odd
        emit updateNeeded();
}

void Filter_Plugin::on_mainTabs_currentChanged(int index)
{
    Q_UNUSED(index);
    emit updateNeeded();
}

void Filter_Plugin::on_derivSobelRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Filter_Plugin::on_derivScharrRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Filter_Plugin::on_derivLaplacRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Filter_Plugin::on_derivScaleSpin_valueChanged(double arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_derivDeltaSpin_valueChanged(double arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_morphErodeRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Filter_Plugin::on_morphDilateRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Filter_Plugin::on_morphMorphRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Filter_Plugin::on_morphIterSpin_valueChanged(int arg1)
{
    Q_UNUSED(arg1);
    emit updateNeeded();
}

void Filter_Plugin::on_morphTypesCombo_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    emit updateNeeded();
}

void Filter_Plugin::on_morphShapesCombo_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    emit updateNeeded();
}
图像转变插件 
transform_plugin.h
#ifndef TRANSFORM_PLUGIN_H
#define TRANSFORM_PLUGIN_H

#include "transform_plugin_global.h"
#include "cvplugininterface.h"
#include <QObject>
#include <QtMath>

namespace Ui {
    class PluginGui;
}

class TRANSFORM_PLUGINSHARED_EXPORT Transform_Plugin: public QObject, public CvPluginInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "com.computervision.cvplugininterface")
    Q_INTERFACES(CvPluginInterface)
public:
    Transform_Plugin();
    ~Transform_Plugin();

    QString title();
    QString version();
    QString description();
    QString help();
    void setupUi(QWidget *parent);
    void processImage(const cv::Mat &inputImage, cv::Mat &outputImage);

signals:
    void updateNeeded();
    void errorMessage(QString msg);
    void infoMessage(QString msg);

private slots:


    void on_resizeHalfRadio_toggled(bool checked);

    void on_resizeDoubleRadio_toggled(bool checked);

    void on_remapRadio_toggled(bool checked);

    void on_affineRadio_toggled(bool checked);

    void on_perspectiveRadio_toggled(bool checked);

    void on_borderTypeCombo_currentIndexChanged(int index);

    void on_interpolationCombo_currentIndexChanged(int index);

private:
    Ui::PluginGui *ui;

};

#endif // TRANSFORM_PLUGIN_H
transform_plugin.cpp
#include "transform_plugin.h"

#include "ui_plugin.h"

Transform_Plugin::Transform_Plugin()
{
    // Insert initialization codes here ...
}

Transform_Plugin::~Transform_Plugin()
{
    // Insert cleanup codes here ...
}

QString Transform_Plugin::title()
{
    return this->metaObject()->className();
}

QString Transform_Plugin::version()
{
    return "1.0.0";
}

QString Transform_Plugin::description()
{
    return "Performs different transformations available in OpenCV";
}

QString Transform_Plugin::help()
{
    return "This is a plugin that performs different filters available in OpenCV";
}

void Transform_Plugin::setupUi(QWidget *parent)
{
    ui = new Ui::PluginGui;
    ui->setupUi(parent);

    ui->borderTypeCombo->addItems(
                QStringList()
                << "BORDER_CONSTANT"
                << "BORDER_REPLICATE"
                << "BORDER_REFLECT"
                << "BORDER_WRAP"
                << "BORDER_REFLECT_101");

    ui->interpolationCombo->addItems(
                QStringList()
                << "INTER_NEAREST"
                << "INTER_CUBIC"
                << "INTER_AREA"
                << "INTER_LANCZOS4");

    connect(ui->resizeHalfRadio, SIGNAL(toggled(bool)), this, SLOT(on_resizeHalfRadio_toggled(bool)));
    connect(ui->resizeDoubleRadio, SIGNAL(toggled(bool)), this, SLOT(on_resizeDoubleRadio_toggled(bool)));
    connect(ui->remapRadio, SIGNAL(toggled(bool)), this, SLOT(on_remapRadio_toggled(bool)));
    connect(ui->affineRadio, SIGNAL(toggled(bool)), this, SLOT(on_affineRadio_toggled(bool)));
    connect(ui->perspectiveRadio, SIGNAL(toggled(bool)), this, SLOT(on_perspectiveRadio_toggled(bool)));
    connect(ui->borderTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(on_borderTypeCombo_currentIndexChanged(int)));
    connect(ui->interpolationCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(on_interpolationCombo_currentIndexChanged(int)));
}

void Transform_Plugin::processImage(const cv::Mat &inputImage, cv::Mat &outputImage)
{
    using namespace cv;
    if(ui->resizeHalfRadio->isChecked())
    {
        resize(inputImage,
               outputImage,
               Size(),
               0.5,
               0.5,
               ui->interpolationCombo->currentIndex());
    }
    else if(ui->resizeDoubleRadio->isChecked())
    {
        resize(inputImage,
               outputImage,
               Size(),
               2.0,
               2.0,
               ui->interpolationCombo->currentIndex());
    }
    else if(ui->affineRadio->isChecked())
    {
        Point2f triangleA[3];
        Point2f triangleB[3];

        triangleA[0] = Point2f(0, 0);
        triangleA[1] = Point2f(inputImage.cols - 1, 0);
        triangleA[2] = Point2f(0, inputImage.rows - 1);

        triangleB[0] = Point2f(inputImage.cols*0.0, inputImage.rows*0.33);
        triangleB[1] = Point2f(inputImage.cols*0.85, inputImage.rows*0.25);
        triangleB[2] = Point2f(inputImage.cols*0.15, inputImage.rows*0.7);

        Mat affineMat = getAffineTransform( triangleA, triangleB );

        warpAffine( inputImage,
                    outputImage,
                    affineMat,
                    inputImage.size(),
                    ui->interpolationCombo->currentIndex(),
                    ui->borderTypeCombo->currentIndex());
    }
    else if(ui->perspectiveRadio->isChecked())
    {
        std::vector<Point2f> cornersA(4);
        std::vector<Point2f> cornersB(4);

        cornersA[0] = Point2f(0, 0);
        cornersA[1] = Point2f(inputImage.cols, 0);
        cornersA[2] = Point2f(inputImage.cols, inputImage.rows);
        cornersA[3] = Point2f(0, inputImage.rows);

        cornersB[0] = Point2f(inputImage.cols*0.25, 0);
        cornersB[1] = Point2f(inputImage.cols * 0.90, 0);
        cornersB[2] = Point2f(inputImage.cols, inputImage.rows);
        cornersB[3] = Point2f(0, inputImage.rows * 0.80);

        Mat homo = findHomography(cornersA, cornersB, RANSAC);
        warpPerspective(inputImage,
                        outputImage,
                        homo,
                        inputImage.size(),
                        ui->interpolationCombo->currentIndex(),
                        ui->borderTypeCombo->currentIndex()); // do perspective transformation
    }
    else if(ui->remapRadio->isChecked())
    {
        cvtColor(inputImage, outputImage, CV_32FC(1));;
        Mat mapX, mapY;
        mapX.create(inputImage.size(), CV_32FC(1));
        mapY.create(inputImage.size(), CV_32FC(1));

        Point2f center(inputImage.cols/2,
                       inputImage.rows/2);

        for(int i=0; i<inputImage.rows; i++)
            for(int j=0; j<inputImage.cols; j++)
            {
                double x = j - center.x;
                double y = i - center.y;

                x = x*x/500;
                /* y = y; */

                mapX.at<float>(i,j) = x + center.x;
                mapY.at<float>(i,j) = y + center.y;
            }

        remap(inputImage,
              outputImage,
              mapX,
              mapY,
              INTER_LANCZOS4,
              BORDER_CONSTANT);

    }
}

void Transform_Plugin::on_resizeHalfRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Transform_Plugin::on_resizeDoubleRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Transform_Plugin::on_remapRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Transform_Plugin::on_affineRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Transform_Plugin::on_perspectiveRadio_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Transform_Plugin::on_borderTypeCombo_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    emit updateNeeded();
}

void Transform_Plugin::on_interpolationCombo_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    emit updateNeeded();
}
颜色与空间插件 
color_plugin.h
#ifndef COLOR_PLUGIN_H
#define COLOR_PLUGIN_H

#include "color_plugin_global.h"
#include "cvplugininterface.h"

namespace Ui {
    class PluginGui;
}

class COLOR_PLUGINSHARED_EXPORT Color_Plugin: public QObject, public CvPluginInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "com.computervision.cvplugininterface")
    Q_INTERFACES(CvPluginInterface)
public:
    Color_Plugin();
    ~Color_Plugin();

    QString title();
    QString version();
    QString description();
    QString help();
    void setupUi(QWidget *parent);
    void processImage(const cv::Mat &inputImage, cv::Mat &outputImage);

signals:
    void updateNeeded();
    void errorMessage(QString msg);
    void infoMessage(QString msg);

private slots:
    void on_colorMapCombo_currentIndexChanged(int index);

private:
    Ui::PluginGui *ui;

};

#endif // COLOR_PLUGIN_H
color_plugin.cpp 
#include "color_plugin.h"

#include "ui_plugin.h"

Color_Plugin::Color_Plugin()
{
    // Insert initialization codes here ...
}

Color_Plugin::~Color_Plugin()
{
    // Insert cleanup codes here ...
}

QString Color_Plugin::title()
{
    return this->metaObject()->className();
}

QString Color_Plugin::version()
{
    return "1.0.0";
}

QString Color_Plugin::description()
{
    return "";
}

QString Color_Plugin::help()
{
    return "";
}

void Color_Plugin::setupUi(QWidget *parent)
{
    ui = new Ui::PluginGui;
    ui->setupUi(parent);

    ui->colorMapCombo->addItems(QStringList()
                                << "COLORMAP_AUTUMN"
                                << "COLORMAP_BONE"
                                << "COLORMAP_JET"
                                << "COLORMAP_WINTER"
                                << "COLORMAP_RAINBOW"
                                << "COLORMAP_OCEAN"
                                << "COLORMAP_SUMMER"
                                << "COLORMAP_SPRING"
                                << "COLORMAP_COOL"
                                << "COLORMAP_HSV"
                                << "COLORMAP_PINK"
                                << "COLORMAP_HOT"
                                << "COLORMAP_PARULA");

    connect(ui->colorMapCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(on_colorMapCombo_currentIndexChanged(int)));
}

void Color_Plugin::processImage(const cv::Mat &inputImage, cv::Mat &outputImage)
{
    using namespace cv;
    applyColorMap(inputImage, outputImage, ui->colorMapCombo->currentIndex());
}

void Color_Plugin::on_colorMapCombo_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    emit updateNeeded();
}

阈值化插件

segmentation_plugin.h

#ifndef SEGMENTATION_PLUGIN_H
#define SEGMENTATION_PLUGIN_H

#include "segmentation_plugin_global.h"
#include "cvplugininterface.h"

namespace Ui {
    class PluginGui;
}

class SEGMENTATION_PLUGINSHARED_EXPORT Segmentation_Plugin: public QObject, public CvPluginInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "com.computervision.cvplugininterface")
    Q_INTERFACES(CvPluginInterface)
public:
    Segmentation_Plugin();
    ~Segmentation_Plugin();

    QString title();
    QString version();
    QString description();
    QString help();
    void setupUi(QWidget *parent);
    void processImage(const cv::Mat &inputImage, cv::Mat &outputImage);

signals:
    void updateNeeded();
    void errorMessage(QString msg);
    void infoMessage(QString msg);

private slots:
    void on_threshAdaptiveCheck_toggled(bool checked);

    void on_threshAdaptiveCombo_currentIndexChanged(int index);

    void on_threshTypeCombo_currentIndexChanged(int index);

    void on_threshSlider_valueChanged(int value);

    void on_threshMaxSlider_valueChanged(int value);

private:
    Ui::PluginGui *ui;

};

#endif // SEGMENTATION_PLUGIN_H

segmentation_plugin.cpp

#include "segmentation_plugin.h"
#include <opencv2\imgproc\types_c.h>
#include "ui_plugin.h"

Segmentation_Plugin::Segmentation_Plugin()
{
    // Insert initialization codes here ...
}

Segmentation_Plugin::~Segmentation_Plugin()
{
    // Insert cleanup codes here ...
}

QString Segmentation_Plugin::title()
{
    return this->metaObject()->className();
}

QString Segmentation_Plugin::version()
{
    return "1.0.0";
}

QString Segmentation_Plugin::description()
{
    return "";
}

QString Segmentation_Plugin::help()
{
    return "";
}

void Segmentation_Plugin::setupUi(QWidget *parent)
{
    ui = new Ui::PluginGui;
    ui->setupUi(parent);

    ui->threshAdaptiveCombo->addItems(
                QStringList()
                << "ADAPTIVE_THRESH_MEAN_C"
                << "ADAPTIVE_THRESH_GAUSSIAN_C");

    ui->threshTypeCombo->addItems(
                QStringList()
                << "THRESH_BINARY"
                << "THRESH_BINARY_INV"
                << "THRESH_TRUNC"
                << "THRESH_TOZERO"
                << "THRESH_TOZERO_INV");
    connect(ui->threshAdaptiveCheck, SIGNAL(toggled(bool)), this, SLOT(on_threshAdaptiveCheck_toggled(bool)));
    connect(ui->threshAdaptiveCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(on_threshAdaptiveCombo_currentIndexChanged(int)));
    connect(ui->threshTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(on_threshTypeCombo_currentIndexChanged(int)));
    connect(ui->threshSlider, SIGNAL(valueChanged(int)), this, SLOT(on_threshSlider_valueChanged(int)));
    connect(ui->threshMaxSlider, SIGNAL(valueChanged(int)), this, SLOT(on_threshMaxSlider_valueChanged(int)));

}

void Segmentation_Plugin::processImage(const cv::Mat &inputImage, cv::Mat &outputImage)
{
    using namespace cv;

    Mat grayScale;
    cvtColor(inputImage, grayScale, CV_BGR2GRAY);

    if(ui->threshAdaptiveCheck->isChecked())
    {
        adaptiveThreshold(grayScale,
                          grayScale,
                          ui->threshMaxSlider->value(),
                          ui->threshAdaptiveCombo->currentIndex(),
                          ui->threshTypeCombo->currentIndex(),
                          7,
                          0);
    }
    else
    {
        threshold(grayScale,
                  grayScale,
                  ui->threshSlider->value(),
                  ui->threshMaxSlider->value(),
                  ui->threshTypeCombo->currentIndex());
    }
    cvtColor(grayScale, outputImage, CV_GRAY2BGR);
}

void Segmentation_Plugin::on_threshAdaptiveCheck_toggled(bool checked)
{
    Q_UNUSED(checked);
    emit updateNeeded();
}

void Segmentation_Plugin::on_threshAdaptiveCombo_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    emit updateNeeded();
}

void Segmentation_Plugin::on_threshTypeCombo_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    emit updateNeeded();
}

void Segmentation_Plugin::on_threshSlider_valueChanged(int value)
{
    emit infoMessage(QString::number(value));
    emit updateNeeded();
}

void Segmentation_Plugin::on_threshMaxSlider_valueChanged(int value)
{
    emit infoMessage(QString::number(value));
    emit updateNeeded();
}

主界面工程 mainapp.pro


QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = Computer_Vision
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

#win环境配置
win32:{
    CONFIG(debug, debug|release){
        DESTDIR += $$PWD/../../bin/win64d
        LIBS += -L$$PWD/../../lib/win64d -lopencv_world480d
    }else{
        DESTDIR += $$PWD/../../bin/win64
        LIBS += -L$$PWD/../../lib/win64 -lopencv_world480
    }
}

INCLUDEPATH += ../../Include

INCLUDEPATH += ../cvplugininterface

SOURCES += \
        main.cpp \
        mainwindow.cpp \
    qenhancedgraphicsview.cpp

HEADERS += \
        mainwindow.h \
    qenhancedgraphicsview.h

FORMS += \
        mainwindow.ui

win32: {
    include("c:/dev/opencv/opencv.pri")
}

unix: !macx{
    CONFIG += link_pkgconfig
    PKGCONFIG += opencv
}

unix: macx{
INCLUDEPATH += /usr/local/include
LIBS += -L"/usr/local/lib" \
    -lopencv_world
}

# Add more language entries here, following the same naming rule
TRANSLATIONS = language_tr.ts
qenhancedgraphicsview.h
#ifndef QENHANCEDGRAPHICSVIEW_H
#define QENHANCEDGRAPHICSVIEW_H

#include <QWidget>
#include <QGraphicsView>
#include <QWheelEvent>
#include <QMouseEvent>
#include <QtMath>
#include <QContextMenuEvent>
#include <QMenu>
#include <QGraphicsItem>
#include <QDebug>
#include <QGraphicsEffect>

class QEnhancedGraphicsView : public QGraphicsView
{
    Q_OBJECT
public:
    explicit QEnhancedGraphicsView(QWidget *parent = nullptr);

protected:
    void wheelEvent(QWheelEvent *event);

signals:

public slots:

private:
    QPointF sceneMousePos;

};

#endif // QENHANCEDGRAPHICSVIEW_H
qenhancedgraphicsview.cpp
#include "qenhancedgraphicsview.h"

QEnhancedGraphicsView::QEnhancedGraphicsView(QWidget *parent)
    : QGraphicsView(parent)
{

}

void QEnhancedGraphicsView::wheelEvent(QWheelEvent *event)
{
    if (event->orientation() == Qt::Vertical)
    {
        double angleDeltaY = event->angleDelta().y();
        double zoomFactor = qPow(1.0015, angleDeltaY);
        scale(zoomFactor, zoomFactor);
        if(angleDeltaY > 0)
        {
            this->centerOn(sceneMousePos);
            sceneMousePos = this->mapToScene(event->pos());
        }
        this->viewport()->update();
        event->accept();
    }
    else
    {
        event->ignore();
    }
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSettings>
#include <QCloseEvent>
#include <QMessageBox>
#include <QDir>
#include <QFileInfoList>
#include <QLibrary>
#include <QPluginLoader>
#include <QDebug>
#include <QFileDialog>
#include <QLabel>
#include <QGraphicsScene>
#include <QPushButton>
#include <QGraphicsProxyWidget>
#include <QTranslator>
#include <QThread>
#include <QThreadPool>
#include <QRunnable>
#include <QMutex>
#include <QWaitCondition>

#include "cvplugininterface.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

protected:
    void closeEvent(QCloseEvent *event);
    void changeEvent(QEvent *event);

private slots:
    void onPluginActionTriggered(bool);
    void onLanguageActionTriggered(bool);
    void onThemeActionTriggered(bool);
    void onCurrentPluginUpdateNeeded();
    void onCurrentPluginErrorMessage(QString msg);
    void onCurrentPluginInfoMessage(QString msg);

    void on_actionAboutQt_triggered();

    void on_actionExit_triggered();

    void on_actionOpenImage_triggered();

    void on_viewOriginalCheck_toggled(bool checked);

    void on_actionSaveImage_triggered();

    void on_action_Camera_triggered();

private:
    Ui::MainWindow *ui;

    void loadSettings();
    void saveSettings();

    QString currentThemeFile;
    QString currentLanguageFile;
    QString currentPluginFile;

    void populatePluginsMenu();
    void populateLanguagesMenu();
    void populateThemesMenu();

    QPointer<QPluginLoader> currentPlugin;
    QPointer<QWidget> currentPluginGui;
    QGraphicsScene scene;
    QTranslator translator;
    QGraphicsPixmapItem originalPixmap, processedPixmap;
    cv::Mat originalMat, processedMat;
    QImage originalImage, processedImage;

};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

#define PLUGINS_SUBFOLDER                   "/cvplugins/"
//#define PLUGINS_SUBFOLDER                   "/filter_plugins/"
#define LANGUAGES_SUBFOLDER                 "/languages/"
#define THEMES_SUBFOLDER                    "/themes/"
#define FILE_ON_DISK_DYNAMIC_PROPERTY       "absolute_file_path"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    loadSettings(); // First thing's first, load settings

    //加载插件菜单
    populatePluginsMenu();
    //加载多语言菜单
    populateLanguagesMenu();
    //加载主题菜单
    populateThemesMenu();

    ui->graphicsView->setScene(&scene);
    scene.addItem(&originalPixmap);
    scene.addItem(&processedPixmap);

    ui->graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
}

MainWindow::~MainWindow()
{
    delete currentPlugin;
    delete ui;
}

void MainWindow::loadSettings()
{
    QSettings settings("Packt", "Computer_Vision", this);
    currentThemeFile = settings.value("currentThemeFile", "").toString();
    currentLanguageFile = settings.value("currentLanguageFile", "").toString();
    currentPluginFile = settings.value("currentPluginFile", "").toString();
}

void MainWindow::saveSettings()
{
    QSettings settings("Packt", "Computer_Vision", this);
    settings.setValue("currentThemeFile", currentThemeFile);
    settings.setValue("currentLanguageFile", currentLanguageFile);
    settings.setValue("currentPluginFile", currentPluginFile);
}

void MainWindow::changeEvent(QEvent *event)
{
    if(event->type() == QEvent::LanguageChange)
    {
        ui->retranslateUi(this);
    }
    else
    {
        QMainWindow::changeEvent(event);
    }
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    int result = QMessageBox::warning(this, tr("Exit"), tr("Are you sure you want to exit?"), QMessageBox::Yes, QMessageBox::No);
    if(result == QMessageBox::Yes)
    {
        saveSettings();
        event->accept();
    }
    else
    {
        event->ignore();
    }
}

void MainWindow::populatePluginsMenu()
{
    // Load all plugins and populate the menus
    QDir pluginsDir(qApp->applicationDirPath() + PLUGINS_SUBFOLDER);
    //QDir pluginsDir(qApp->applicationDirPath());
    QFileInfoList pluginFiles = pluginsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name);
    qDebug() << "MainWindow::populatePluginsMenu=====================pluginFiles.size========" << pluginFiles.size();
    foreach(QFileInfo pluginFile, pluginFiles)
    {
        if(QLibrary::isLibrary(pluginFile.absoluteFilePath()))
        {
            QPluginLoader pluginLoader(pluginFile.absoluteFilePath(), this);
            if(CvPluginInterface *plugin = dynamic_cast<CvPluginInterface*>(pluginLoader.instance()))
            {
                QAction *pluginAction = ui->menu_Plugins->addAction(plugin->title());
                pluginAction->setProperty(FILE_ON_DISK_DYNAMIC_PROPERTY, pluginFile.absoluteFilePath());
                connect(pluginAction, SIGNAL(triggered(bool)), this, SLOT(onPluginActionTriggered(bool)));
                if(currentPluginFile == pluginFile.absoluteFilePath())
                {
                    pluginAction->trigger();
                }
            }
            else
            {
                QMessageBox::warning(this, tr("Warning"),
                                     QString(tr("Make sure %1 is a correct plugin for this application<br>"
                                                "and it's not in use by some other application!")).arg(pluginFile.fileName()));
            }
        }
        else
        {
            QMessageBox::warning(this, tr("Warning"),
                                 QString(tr("Make sure only plugins exist in %1 folder.<br>"
                                            "%2 is not a plugin."))
                                 .arg(PLUGINS_SUBFOLDER)
                                 .arg(pluginFile.fileName()));
        }
    }

    if(ui->menu_Plugins->actions().count() <= 0)
    {
        QMessageBox::critical(this, tr("No Plugins"), QString(tr("This application cannot work without plugins!"
                                                                 "<br>Make sure that %1 folder exists "
                                                                 "in the same folder as the application<br>and that "
                                                                 "there are some filter plugins inside it")).arg(PLUGINS_SUBFOLDER));
        this->setEnabled(false);
    }
}

void MainWindow::populateLanguagesMenu()
{
    QMenu *languagesMenu = new QMenu(this);
    // Add default (english) language
    QAction *defaultLanguageAction = languagesMenu->addAction("English - US");
    defaultLanguageAction->setProperty(FILE_ON_DISK_DYNAMIC_PROPERTY, "");
    connect(defaultLanguageAction, SIGNAL(triggered(bool)), this, SLOT(onLanguageActionTriggered(bool)));

    // Load all languages and populate the menus
    QDir languagesDir(qApp->applicationDirPath() + LANGUAGES_SUBFOLDER);
    QFileInfoList languageFiles = languagesDir.entryInfoList(QStringList() << "*.qm", QDir::NoDotAndDotDot | QDir::Files, QDir::Name);
    foreach(QFileInfo languageFile, languageFiles)
    {
        QAction *languageAction = languagesMenu->addAction(languageFile.baseName());
        languageAction->setProperty(FILE_ON_DISK_DYNAMIC_PROPERTY, languageFile.absoluteFilePath());
        connect(languageAction, SIGNAL(triggered(bool)), this, SLOT(onLanguageActionTriggered(bool)));

        if(currentLanguageFile == languageFile.absoluteFilePath())
        {
            languageAction->trigger();
        }
    }
    ui->actionLanguage->setMenu(languagesMenu);
}

void MainWindow::populateThemesMenu()
{
    QMenu *themesMenu = new QMenu(this);
    // Add default (native) theme
    QAction *defaultThemeAction = themesMenu->addAction("Default");
    defaultThemeAction->setProperty(FILE_ON_DISK_DYNAMIC_PROPERTY, "");
    connect(defaultThemeAction, SIGNAL(triggered(bool)), this, SLOT(onThemeActionTriggered(bool)));

    // Load all themes and populate the menus
    QDir themesDir(qApp->applicationDirPath() + THEMES_SUBFOLDER);
    QFileInfoList themeFiles = themesDir.entryInfoList(QStringList() << "*.thm", QDir::NoDotAndDotDot | QDir::Files, QDir::Name);
    foreach(QFileInfo themeFile, themeFiles)
    {
        QAction *themeAction = themesMenu->addAction(themeFile.baseName());
        themeAction->setProperty(FILE_ON_DISK_DYNAMIC_PROPERTY, themeFile.absoluteFilePath());
        connect(themeAction, SIGNAL(triggered(bool)), this, SLOT(onThemeActionTriggered(bool)));

        if(currentThemeFile == themeFile.absoluteFilePath())
        {
            themeAction->trigger();
        }
    }
    ui->actionTheme->setMenu(themesMenu);
}

void MainWindow::on_actionAboutQt_triggered()
{
    qApp->aboutQt();
}

void MainWindow::on_actionExit_triggered()
{
    close();
}

//加载插件
void MainWindow::onPluginActionTriggered(bool)
{
    if(!currentPlugin.isNull())
    {
        delete currentPlugin;
        delete currentPluginGui;
    }
    qDebug() << "MainWindow::onPluginActionTriggered====================currentPlugin========" << currentPlugin.isNull();
    currentPluginFile = QObject::sender()->property(FILE_ON_DISK_DYNAMIC_PROPERTY).toString();
    currentPlugin = new QPluginLoader(currentPluginFile, this);
    currentPluginGui = new QWidget(this);
    ui->pluginLayout->addWidget(currentPluginGui);
    CvPluginInterface *currentPluginInstance = dynamic_cast<CvPluginInterface*>(currentPlugin->instance());

    if(currentPluginInstance)
    {
        currentPluginInstance->setupUi(currentPluginGui);
        connect(currentPlugin->instance(), SIGNAL(updateNeeded()), this, SLOT(onCurrentPluginUpdateNeeded()));
        connect(currentPlugin->instance(), SIGNAL(infoMessage(QString)), this, SLOT(onCurrentPluginInfoMessage(QString)));
        connect(currentPlugin->instance(), SIGNAL(errorMessage(QString)), this, SLOT(onCurrentPluginErrorMessage(QString)));

    }
}

void MainWindow::onLanguageActionTriggered(bool)
{
    currentLanguageFile = QObject::sender()->property(FILE_ON_DISK_DYNAMIC_PROPERTY).toString();
    qApp->removeTranslator(&translator);
    if(!currentLanguageFile.isEmpty())
    {
        translator.load(currentLanguageFile);
        qApp->installTranslator(&translator);
        ui->retranslateUi(this);
    }
}

void MainWindow::onThemeActionTriggered(bool)
{
    currentThemeFile = QObject::sender()->property(FILE_ON_DISK_DYNAMIC_PROPERTY).toString();
    QFile themeFile(currentThemeFile);
    if(currentThemeFile.isEmpty())
    {
        qApp->setStyleSheet("");
    }
    else
    {
        themeFile.open(QFile::ReadOnly | QFile::Text);
        QString styleSheet = themeFile.readAll();
        qApp->setStyleSheet(styleSheet);
        themeFile.close();
    }
}

void MainWindow::on_actionOpenImage_triggered()
{
    QString fileName = QFileDialog::getOpenFileName(this,
                                                    tr("Open Input Image"),
                                                    QDir::currentPath(),
                                                    tr("Images") + " (*.jpg *.png *.bmp)");

    using namespace cv;
    originalMat = imread(fileName.toStdString());
    if(!originalMat.empty())
    {
        onCurrentPluginUpdateNeeded();
    }
    else if(!fileName.trimmed().isEmpty())
    {
        QMessageBox::critical(this,
                              tr("Error"),
                              tr("Make sure the image file exists "
                                 "and it is accessible!"));
    }
}
//是否显示原图
void MainWindow::on_viewOriginalCheck_toggled(bool checked)
{
    originalPixmap.setVisible(checked);
    processedPixmap.setVisible(!checked);
}

void MainWindow::onCurrentPluginUpdateNeeded()
{
    if(!originalMat.empty())
    {
        if(!currentPlugin.isNull())
        {
            CvPluginInterface *currentPluginInstance = dynamic_cast<CvPluginInterface*>(currentPlugin->instance());
            if(currentPluginInstance)
            {
                cv::TickMeter meter;
                meter.start();
                //这里是插件的处理函数,输入:originalMat===》输出:processedMat
                currentPluginInstance->processImage(originalMat, processedMat);
                meter.stop();
                qDebug() << "The process took " << meter.getTimeMilli() << "milliseconds";
            }
        }
        else
        {
            processedMat = originalMat.clone();
        }

        originalImage = QImage(originalMat.data, originalMat.cols, originalMat.rows, originalMat.step, QImage::Format_RGB888);
        originalPixmap.setPixmap(QPixmap::fromImage(originalImage.rgbSwapped()));

        processedImage = QImage(processedMat.data, processedMat.cols, processedMat.rows, processedMat.step, QImage::Format_RGB888);
        processedPixmap.setPixmap(QPixmap::fromImage(processedImage.rgbSwapped()));
    }
}

void MainWindow::on_actionSaveImage_triggered()
{
    if(!ui->viewOriginalCheck->isChecked() && !processedMat.empty())
    {
        QString fileName = QFileDialog::getSaveFileName(this, tr("Save Image"), QDir::currentPath(), "*.jpg;;*.png;;*.bmp");
        if(!fileName.isEmpty())
        {
            cv::imwrite(fileName.toStdString(), processedMat);
        }
    }
    else if(ui->viewOriginalCheck->isChecked() && !originalMat.empty())
    {
        QString fileName = QFileDialog::getSaveFileName(this, tr("Save Image"), QDir::currentPath(), "*.jpg;;*.png;;*.bmp");
        if(!fileName.isEmpty())
        {
            cv::imwrite(fileName.toStdString(), originalMat);
        }
    }
    else
    {
        QMessageBox::warning(this, tr("Warning"), tr("There is nothing to be saved!"));
    }
}

void MainWindow::onCurrentPluginErrorMessage(QString msg)
{
    qDebug() << "Plugin Error Message : " << msg;
}

void MainWindow::onCurrentPluginInfoMessage(QString msg)
{
    qDebug() << "Plugin Info Message : " << msg;
}

void MainWindow::on_action_Camera_triggered()
{

}

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    a.setStyle("fusion");
    MainWindow w;
    w.showMaximized();
    return a.exec();
}

完整理项目

https://download.csdn.net/download/chenyijun/88904514

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值