本章将介绍以下主题:
如何为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();
}
完整理项目