四小时学习opencv+qt系列(第三天)
一、接口是一个什么也不做的类,只列出应用程序所需要的所有插件的草图,一定要在一开始的时候将所有必须的函数都包含在插件接口中。首先建立一个.h文件,命名为cvplugininterface.h,内容如下
#ifndef CVPLUGININTERFACE_H
#define CVPLUGININTERFACE_H
#include
#include
#include "opencv2/opencv.hpp"
class CvPluginInterface
{
public:
virtual ~CvPluginInterface() {}
virtual QString description() = 0;
virtual void processImage(const cv::Mat &inputImage, cv::Mat &outputImage) = 0;
};
#define CVPLUGININTERFACE_IID "com.amin.cvplugininterface"
Q_DECLARE_INTERFACE(CvPluginInterface, CVPLUGININTERFACE_IID)
#endif // CVPLUGININTERFACE_H
分析:
ifndef define endif 确保了在处理过程中对每个头文件只进行一次包含和处理。
virtual ~CvPluginInterface() {}是虚析构函数,避免内存泄漏。
description()返回所有插件的描数及其相关有用信息。
processImage函数将cv::Mat &inputImage作为输入,并返回1最为输出。
Q_DECLARE_INTERFACE(CvPluginInterface, CVPLUGININTERFACE_IID)使用Q_DECLARE_INTERFACE将类宏定义为接口
二、插件,创建median_filter_plugin插件
1.项目建好了以后,把cvplugininterface.h放到工程目录下并右键添加现有文件把他加进来。此时的工程目录结构如下:
2.此时要告诉qt这是一个插件而并非只是库,在.pro文件中添加:
CONFIG += plugin
将opencv的路径添加进去:(debug下)
LIBS += -LD:/opencv4.2/opencv/newbuild/newbuild/install/x64/vc14/lib/ -lopencv_world420d
INCLUDEPATH += D:/opencv4.2/opencv/newbuild/newbuild/install/include/
D:/opencv4.2/opencv/newbuild/newbuild/install/include/opencv2/
3.修改median_filter_plugin.h如下
#ifndef MEDIAN_FILTER_PLUGIN_H
#define MEDIAN_FILTER_PLUGIN_H
#include "median_filter_plugin_global.h"
#include "cvplugininterface.h"
class MEDIAN_FILTER_PLUGINSHARED_EXPORT Median_filter_plugin: public QObject, public CvPluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.amin.cvplugininterface")
Q_INTERFACES(CvPluginInterface)
public:
Median_filter_plugin();
~Median_filter_plugin();
QString description();
void processImage(const cv::Mat &inputImage, cv::Mat &outputImage);
};
#endif // MEDIAN_FILTER_PLUGIN_H
4.此时编译可能会提示使用未定义的类class MEDIAN_FILTER_PLUGINSHARED_EXPORT,这里将median_filter_plugin_global.h修改如下就好了
#ifndef MEDIAN_FILTER_PLUGIN_GLOBAL_H
#define MEDIAN_FILTER_PLUGIN_GLOBAL_H
#include
#if defined(MEDIAN_FILTER_PLUGIN_LIBRARY)
# define MEDIAN_FILTER_PLUGINSHARED_EXPORT Q_DECL_EXPORT
#else
# define MEDIAN_FILTER_PLUGINSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // MEDIAN_FILTER_PLUGIN_GLOBAL_H
5.接下来修改median_filter_plugin.cpp如下:这里就是类析构函数的实现以及description()和processImage函数。
#include "median_filter_plugin.h"
Median_filter_plugin::Median_filter_plugin()
{
}
Median_filter_plugin::~Median_filter_plugin()
{
}
QString Median_filter_plugin::description()
{
return "This plugin applies median blur filters to any image."
" This plugin's goal is to make us more familiar with the"
" concept of plugins in general.";
}
void Median_filter_plugin::processImage(const cv::Mat &inputImage, cv::Mat &outputImage)
{
cv::medianBlur(inputImage, outputImage, 5);
}
6.接下来Build就行了。可以看到生成的目录里:
三、接下插件加载器和用户
1.新建工程Plugin_User,基类那里选择QMainWindow
2.ui界面:
3.将之前的cvplugininterface.h添加到工程中。
4.mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#include
#include "opencv2/opencv.hpp"
#include "cvplugininterface.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_inputImgButton_pressed();
void on_helpButton_pressed();
void on_filterButton_pressed();
private:
Ui::MainWindow *ui;
void getPluginsList();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#define FILTERS_SUBFOLDER "/filter_plugins/"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
getPluginsList();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::getPluginsList()
{
QDir filtersDir(qApp->applicationDirPath() + FILTERS_SUBFOLDER);
QFileInfoList filters = filtersDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name);
foreach(QFileInfo filter, filters)
{
if(QLibrary::isLibrary(filter.absoluteFilePath()))
{
QPluginLoader pluginLoader(filter.absoluteFilePath(), this);
if(dynamic_cast(pluginLoader.instance()))
{
ui->filtersList->addItem(filter.fileName());
pluginLoader.unload(); // we can unload for now
}
else
{
QMessageBox::warning(this, tr("Warning"),
QString(tr("Make sure %1 is a correct plugin for this application
"
"and it's not in use by some other application!")).arg(filter.fileName()));
}
}
else
{
QMessageBox::warning(this, tr("Warning"),
QString(tr("Make sure only plugins exist in plugins folder.
"
"%1 is not a plugin.")).arg(filter.fileName()));
}
}
if(ui->filtersList->count() <= 0)
{
QMessageBox::critical(this, tr("No Plugins"), tr("This application cannot work without plugins!"
"
Make sure that filter_plugins folder exists "
"in the same folder as the application
and that "
"there are some filter plugins inside it"));
this->setEnabled(false);
}
}
void MainWindow::on_inputImgButton_pressed()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Input Image"), QDir::currentPath(), tr("Images") + " (*.jpg *.png *.bmp)");
if(QFile::exists(fileName))
{
ui->inputImgEdit->setText(fileName);
}
}
void MainWindow::on_helpButton_pressed()
{
if(ui->filtersList->currentRow() >= 0)
{
QPluginLoader pluginLoader(qApp->applicationDirPath() + FILTERS_SUBFOLDER + ui->filtersList->currentItem()->text());
CvPluginInterface *plugin = dynamic_cast(pluginLoader.instance());
if(plugin)
{
QMessageBox::information(this, tr("Plugin Description"), plugin->description());
}
else
{
QMessageBox::warning(this, tr("Warning"), QString(tr("Make sure plugin %1 exists and is usable.")).arg(ui->filtersList->currentItem()->text()));
}
}
else
{
QMessageBox::warning(this, tr("Warning"), QString(tr("First select a filter plugin from the list.")));
}
}
void MainWindow::on_filterButton_pressed()
{
if(ui->filtersList->currentRow() >= 0 && !ui->inputImgEdit->text().isEmpty())
{
QPluginLoader pluginLoader(qApp->applicationDirPath() + FILTERS_SUBFOLDER + ui->filtersList->currentItem()->text());
CvPluginInterface *plugin = dynamic_cast(pluginLoader.instance());
if(plugin)
{
if(QFile::exists(ui->inputImgEdit->text()))
{
using namespace cv;
Mat inputImage, outputImage;
inputImage = imread(ui->inputImgEdit->text().toStdString());
plugin->processImage(inputImage, outputImage);
imshow(tr("Filtered Image").toStdString(), outputImage);
}
else
{
QMessageBox::warning(this, tr("Warning"), QString(tr("Make sure %1 exists.")).arg(ui->inputImgEdit->text()));
}
}
else
{
QMessageBox::warning(this, tr("Warning"), QString(tr("Make sure plugin %1 exists and is usable.")).arg(ui->filtersList->currentItem()->text()));
}
}
else
{
QMessageBox::warning(this, tr("Warning"), QString(tr("First select a filter plugin from the list.")));
}
}
主要代码分析:(这里只对获取插件的函数进行分析,其余函数简单就不分析了,下面的代码不能直接加到工程中,添加了一些注释,直接移植可能会报错,完整代码参照上面的)
void MainWindow::getPluginsList()
{
QDir filtersDir(qApp->applicationDirPath() + FILTERS_SUBFOLDER);//获取插件的路径
//提取QFileInfoList
QFileInfoList filters = filtersDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name);
//通过foreach遍历文件夹列表,检索每一个文件
foreach(QFileInfo filter, filters)
{
if(QLibrary::isLibrary(filter.absoluteFilePath()))//确保只检索插件
{
QPluginLoader pluginLoader(filter.absoluteFilePath(), this);
if(dynamic_cast(pluginLoader.instance()))//不让任何一个库文件作为插件被接受
{
ui->filtersList->addItem(filter.fileName());
pluginLoader.unload(); // we can unload for now
}
//插件类型不正确弹出的警告
else
{
QMessageBox::warning(this, tr("Warning"),
QString(tr("Make sure %1 is a correct plugin for this application
"
"and it's not in use by some other application!")).arg(filter.fileName()));
}
}
else
{
//确保插件文件夹中只存在插件
QMessageBox::warning(this, tr("Warning"),
QString(tr("Make sure only plugins exist in plugins folder.
"
"%1 is not a plugin.")).arg(filter.fileName()));
}
}
//如果空则没有可用插件
if(ui->filtersList->count() <= 0)
{
QMessageBox::critical(this, tr("No Plugins"), tr("This application cannot work without plugins!"
"
Make sure that filter_plugins folder exists "
"in the same folder as the application
and that "
"there are some filter plugins inside it"));
this->setEnabled(false);
}
}
接下来是插件的加载,在build的目录下新建filter_plugins文件夹,在其中放入我们之前生产的dll,可以测试使用了,效果如下图: