编译zlib-libpng-MathGL结合openCV-Qt-VS2019实现科学计算可视化

本文实现的效果

  1. 运用CMake、VS2019编译zlib、libpng、MathGL,得到静态链接库(debug & release,X64)

在这里插入图片描述

  1. 例1:基于Visual Studio 2019 IDE,运用MathGL和Qt绘制心形函数
    在这里插入图片描述3. 例2:基于Visual Studio 2019 IDE,由openCV读入图像,得到直方图数据,并由MathGL画出直方图,在Qt中显示。
    在这里插入图片描述

软件版本

  • CMake-3.18.0-rc3
  • zlib-1.2.11,源代码
  • libpng-1.6.37,源代码
  • MathGL-2.4.4,源代码
  • openCV-4.3.0
  • Qt-5.14.2
  • Visual Studio 2019-16.4.3,MSVC-19.24.28315.0
  • Qt Visual Studio Tools-2.5.2.1

编译

说明:MathGL依赖zlib和libpng,libpng依赖zlib。因此,按顺序编译zlib、libpng和MathGL。

zlib

  1. 打开CMake-gui

  2. 指定源码目录(1)和编译输出目录(2).
    在这里插入图片描述

  3. 单击Configure进行配置(3)。
    在这里插入图片描述
    (1) 选择合适的Visual Studio版本(4)
    (2) 选择计算机类型(5),本文均以x64平台为例。
    (3) 结束配置Finish(6),会提示“Configuring done”,结果如下图。
    在这里插入图片描述

  4. 单击Generate生成Visual Studio的*.sln项目文件。会提示“Generating done”。

  5. 在(2)所示的输出目录中找到zlib.sln,双击在Visual Studio 2019中打开。

  6. 先生成debug版的静态链接库。

    (1)确认解决方案为debug、X64平台。
    在这里插入图片描述

    (2) 在zlibstatic项目上右击–>生成
    在这里插入图片描述

    (3) Visual Studio 2019的输出窗口会提示生成成功,并给出静态链接库的位置。
    在这里插入图片描述
    (4) 这样,zlib的debug、X64版的静态链接库已经编译成功。为了方便使用,可以将所有的静态链接库和头文件拷贝至统一位置。本文采用的zlib的1.2.11版的源文件中没有include文件夹,头文件直接在根
    (5) 需要注意的是,cmake在configure时会把头文件zlib.h的名字改为zlib.h.include。编译成功后,需要将它改回来。

  7. 再生成release版的静态链接库,只需将编译平台配置为Release、X64,其它步骤相同。
    在这里插入图片描述

  8. CMake的File菜单下有Delete Cache选项,可以清除缓存。清楚缓存后,单击Configure就会弹出选择编译器版本的对话框。

在这里插入图片描述

libpng

  1. 打开CMake-gui,选择源码目录和输出目录,单击Configure进行配置。

  2. 报错,大意为找不到zlib的链接库。单击OK接受这个错误。

在这里插入图片描述
3. 确保勾选Advanced,手动指定ZLIB_INCLUDE_DIR目录和ZLIB_LIBRARY_DEBUG、ZLIB_LIBRARY_RELEASE静态链接库。再Configure,这样就OK了。

在这里插入图片描述4. 单击Generate,生成Visual Studio 2019的*.sln项目。单击Open Project即可打开。
在这里插入图片描述
5. 在Visual Studio 2019中分别以Debug和Release方式编译png_static项目。
6. 可以发现,zlib和libpng的debug静态链接库都要比release版的更大。

MathGL

现在可以编译MathGL,需要依赖zlib、libpng、Qt的头文件及静态链接库。

  1. 打开CMake-gui,选择源码目录和输出目录,单击Configure进行配置。

  2. 报错,无非就是找不到zlib、libpng的头文件和静态链接库,我们手动指定就好。
    在这里插入图片描述
    修改后(在我的计算机中,第一次configure只能找到ZLIB_xxx_xxx的项目,再configure才能找到PNG_xxx_xxx的项目)
    在这里插入图片描述
    在这里插入图片描述

  3. 搜索qt,勾选enable-qt-5的选项,Configure
    在这里插入图片描述

  4. 继续报错,这次是找不到qt的目录。
    在这里插入图片描述
    (1) 手动指定目录,具体路径见图,修改后:
    在这里插入图片描述
    (2) 需要注意Qt匹配的编译器,这里选择的是msvc2017_64文件夹下的。
    (3) 这里有个小坑(虽然这个坑还没有坑我):在CMake中指定的是msvc2017_64文件夹中的路径,意味着这些lib是匹配msvc2017的;而在接下来的编译中,采用的是visual studio 2019,即msvc2019。还要说明另一件事,我给visual studio 2019安装了2017版的工具集,这可能是一种解决方案?或者根本就直接用Visual Studio 2017,别用太新的Visual Studio。

  5. Generate生成*.sln项目文件。忽略这两个不太严重的警告。
    在这里插入图片描述

  6. 打开MathGL2.sln项目文件。编译mgl-static和mgl-qt5-static项目,分别以Debug和Release的形式。

    (1) MathGL的Debug版和Release版的静态链接库的名字是一致的,我们可以给Debug版的静态链接库的名字加个’d’,便于区分。在属性-->常规-->目标文件名处可以修改。

    (2) 生成mgl-static时,我这里出现了一个error

    C:\open\lpng-1.6.37\png.h(330,13): fatal error C1083: 无法打开包括文件: “pnglibconf.h”: No such file or directory
    

    找到C:\open\lpng-1.6.37\png.h(330,13),贴心地提示了一句,我们按它说的去做即可。

    #ifndef PNGLCONF_H
    /* If pnglibconf.h is missing, you can
     * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h
     */
    #   include "pnglibconf.h"
    #endif
    

    (3) MathGL生成的静态链接库的路径比较奇怪,可以Visual Studio 2019输出窗口这一行的提示去找。

    在这里插入图片描述

  7. 编译mgl和mgl-qt5项目可以得到动态链接库。但会出问题,暂时没有深究。

  8. 在CMake的输出目录下的include/mgl2下可以看到三个*.h头文件,将其复制到源文件的include/mgl2目录下。

说明

至此,zlib、libpng和MathGL已经编译完毕,做几点说明。

  • 这是我最顺利的一次编译~
  • 以前为了编译这三个静态链接库,查遍网络,有很多提示是指定预编译头的,但是这里统统没有用上~
  • 在编译MathGL时,遇到的最奇怪的问题时,Visual Studio在生成时提示需要GNU compiler,稀里糊涂就消失了~

例1:心形

目标:MathGL+Qt绘制心形。

  1. 在Visual Studio 2019中创建Qt项目。

    (1) 在Visual Studio 2019中添加Qt插件,配置Qt开发环境(可百度)。

    (2) 选择项目类型:Qt Widgets Application

    在这里插入图片描述

    (3) 指定项目名称、项目位置。

    在这里插入图片描述

    (4) 配置Qt,需要选择X64平台的(因为前面编译的静态链接库是X64平台的)

    在这里插入图片描述

    (5) 文件名可以自行修改。

    在这里插入图片描述

  2. 在Visual Studio 2019中配置包含目录、库目录、附加依赖项。需要分别配置Debug和Release。Debug只能使用Debug的静态链接库,Release只能使用Release的静态链接库。

    (1) 包含目录(Debug)

    在解决方案管理器中的项目名称Heart上右击,选择属性。按下图操作。 在这里插入图片描述

    (2) 库目录(Debug)
    在这里插入图片描述

    (3) 附加依赖项(Debug)
    在这里插入图片描述

    (4) Release与Debug的内容基本一致,需要在库目录中给定Release的zlib、libpng、MathGL静态链接库的路径。

    (5) 配置过程还是比较复杂的,可以将Visual Studio 2019的配置保存到属性表,以便今后使用,具体方法请百度。

  3. 代码

    简便起见,我们直接写在main.cpp中。

    #include "MainWindow.h"
    #include <QtWidgets/QApplication>
    #include <QtWidgets/QMainWindow>
    #include <mgl2/mgl.h>
    #include <mgl2/qmathgl.h>
    
    int heart(mglGraph * gr)
    {
        gr->Title("Heart");
        gr->SetOrigin(0,0); // 原点
        gr->SetRanges(-3.5, 2, -3, 3); // 坐标轴范围
        gr->Axis(); // 开启坐标轴
        gr->Grid(); // 开启坐标网格
        gr->FPlot("2*cos(6.28*t)-cos(6.28*2*t)", "2*sin(6.28*t)-sin(6.28*2*t)", "0", "r2"); // 绘图,函数FPlot绘制参数t在0到1时的参数方程。
        
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QMainWindow* win = new QMainWindow();
        win->resize(800, 600);
        win->setWindowTitle("MathGL-Heart");
    
        QMathGL* qmgl = new QMathGL(win);
        qmgl->setDraw(heart);
        qmgl->update();
    
        win->show();
    
        return a.exec();
    }
    
  4. 以Debug或Release方式运行,会出现很多问题,我们逐一解决。

    (1) 预处理命令问题,表现为:

    LNK2038	检测到“_CRT_STDIO_ISO_WIDE_SPECIFIERS”的不匹配项: 值“1”不匹配值“0”(MainWindow.obj 中)	Heart	G:\008_Practice\005_PracticeCPP\20200713_MathGL-Qt-openCV\Heart\Heart\mgl-qt5-static.lib(mocs_compilation.obj)
    

    解决:进入项目的属性页面,修改预处理命令(Debug和Release分别修改),如下图。在这里插入图片描述

    (2) Qt链接库问题,表现为:

    LNK2019	无法解析的外部符号 "__declspec(dllimport) public: __cdecl QPrinter::QPrinter(enum QPrinter::PrinterMode)" (__imp_??0QPrinter@@QEAA@W4PrinterMode@0@@Z),该符号在函数 "public: void __cdecl QMathGL::print(void)" (?print@QMathGL@@QEAAXXZ) 中被引用	Heart	G:\008_Practice\005_PracticeCPP\20200713_MathGL-Qt-openCV\Heart\Heart\mgl-qt5-static.lib(qt.obj)
    LNK2001	无法解析的外部符号 "public: virtual void __cdecl QPrinter::setPageSizeMM(class QSizeF const &)" (?setPageSizeMM@QPrinter@@UEAAXAEBVQSizeF@@@Z)	Heart	G:\008_Practice\005_PracticeCPP\20200713_MathGL-Qt-openCV\Heart\Heart\mgl-qt5-static.lib(qt.obj)
    

    解决:(Debug和Release分别修改)

    (aa) 属性页面–>包含目录添加:

    C:\Qt\Qt5.14.2\5.14.2\msvc2017_64\include\QtPrintSupport
    

    (bb) 属性页面–>库目录添加:

    C:\Qt\Qt5.14.2\5.14.2\msvc2017_64\lib
    

    (cc) 属性页面–>附加依赖项添加:

    Qt5PrintSupportd.lib
    

    (3) 到这一步,Release已经可以了,但Debug还有一些问题。见下一点。

  5. 在以Debug方式运行时,遇到了玄学问题,如实记录。

    (1) 描述:Visual Studio 2019 提示有两个无法解析的符号

    1>main.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: void __cdecl mglGraph::Title(char const *,char const *,double)" (__imp_?Title@mglGraph@@QEAAXPEBD0N@Z),该符号在函数 "int __cdecl heart(class mglGraph *)" (?heart@@YAHPEAVmglGraph@@@Z) 中被引用
    1>main.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: void __cdecl QMathGL::setDraw(int (__cdecl*)(class mglGraph *))" (__imp_?setDraw@QMathGL@@QEAAXP6AHPEAVmglGraph@@@Z@Z),该符号在函数 main 中被引用
    1>G:\008_Practice\005_PracticeCPP\20200713_MathGL-Qt-openCV\Heart\x64\Debug\Heart.exe : fatal error LNK1120: 2 个无法解析的外部命令
    

    (2) 分析:

    这两个符号分别是int heart(mglGraph * gr)函数中用到的gr->Title("Heart");以及int main(int argc, char *argv[])中用到的qmgl->setDraw(heart);。按道理来说,这两个函数TitlesetDraw应该是在静态链接库mgl-static.libmgl-qt5-static.lib中定义的。

    可以很容易找到Title的头文件mgl.h,该头文件中同时定义了SetOriginSetRangesAxisGridFPlot等函数,这些函数都在int heart(mglGraph * gr)中调用过,都没有问题。

    可以很容易找到setDraw的头文件qmathgl.h,该头文件中同时定义了updateshow等函数,这些函数都在int main(int argc, char *argv[])中调用过,都没有问题。

    (3) 稀里糊涂地解决

    找到Title的函数定义,在mgl.h中,显然我们现在用的是第一种重载类型。

    /// Add title for current subplot/inplot
    /** Style '#' draw box around the title. */
    inline 	void Title(const char *title,const char *stl="",double size=-2)
    {
        mgl_title(gr,title,stl,size);
    }
    /// Add title for current subplot/inplot
    /** Style '#' draw box around the title. */
    inline 	void Title(const wchar_t *title,const char *stl="",double size=-2)
    	{
        mgl_titlew(gr,title,stl,size);
    }
    

    其中,mgl_title定义在canvas_cf.h中的。我们这样修改程序:

    // 在main.cpp中再包含头文件
    #include <mgl2/canvas_cf.h>
    
    // 在int heart(mglGraph * gr)中
    // 将gr->Title("Heart");替换为
    mgl_title((HMGL)gr, "Heart", "", -2);
    

    找到setDraw的头文件qmathgl.h,定义为:

    inline void setDraw(int (*func)(mglGraph *gr))
    {
        setDraw(func?mgl_draw_graph:0,(void*)func);
    }
    

    其中,函数中调用的setDraw仍然定义再qmathgl.h中,mgl_draw_graph声明在wnd.h中。我们这样修改程序:

    // 在main.cpp中再包含头文件
    #include <mgl2/wnd.h>
    
    // 在int main(int argc, char *argv[])中
    // 将qmgl->setDraw(heart);替换为
    qmgl->setDraw(mgl_draw_graph, (void *)heart);
    

    (4) 此时此刻,Debug调试,问题解决。而且,将刚刚include的两个头文件注释掉,并恢复TitlesetDraw为原来的调用方式,仍然没有问题。就很奇怪( ╯□╰ )

    (5) 正确地解决

    添加预处理命令:MGL_STATIC_DEFINE,这个命令说明我们给的lib是静态链接库。

  6. 我们可以很OK地画出一个心形啦!好丑,但就是这个意思 φ(゜▽゜)♪*
    在这里插入图片描述

  7. 配置总结

    (1) 包含目录:

    【MathGL】C:\open\mathgl-2.4.4\include
    【Qt】C:\Qt\Qt5.14.2\5.14.2\msvc2017_64\include\QtPrintSupport
    

    (2) 库目录【Debug】(我把所有自己编译的*.lib都放到一个文件夹了):

    C:\open\lib_Debug_X64
    C:\Qt\Qt5.14.2\5.14.2\msvc2017_64\lib
    

    (3) 库目录【Release】(我把所有自己编译的*.lib都放到一个文件夹了):

    C:\open\lib_Release_X64
    C:\Qt\Qt5.14.2\5.14.2\msvc2017_64\lib
    

    (4) 预处理器定义

    _CRT_STDIO_ISO_WIDE_SPECIFIERS
    MGL_STATIC_DEFINE 
    

    (5) 附加依赖项【Debug】

    mgl-qt5-static-d.lib
    mgl-static-d.lib
    Qt5PrintSupportd.lib
    libpng16_staticd.lib
    zlib-staticd.lib
    

    (6) 附加依赖项【Release】

    zlibstatic.lib
    libpng16_static.lib
    mgl-static.lib
    mgl-qt5-static.lib
    Qt5PrintSupport.lib
    

例2:直方图

  1. 在Visual Studio 2019中创建Qt项目,并配置。

    (1) MathGL、Qt、zlib、libpng的配置与例1相同。
    (2) 还需要对openCV进行配置(详见百度)。主要包括指定openCV的include目录、lib目录、附加依赖项,并将openCV的*.dll文件拷贝至正确位置。

  2. 在Qt Designer中进行可视化窗口设计:1个Widget区域,2个Push Button。 在这里插入图片描述
    采用uic命令将*.ui文件编译为*.h文件,并导入至Visual Studio 2019项目。

  3. 代码

    // main.cpp
    
    
    #include "MainWindow.h"
    #include <QtWidgets/QApplication>
    #include <QtWidgets/QMainWindow>
    
    int main(int argc, char* argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.setWindowTitle("openCV-MathGL-Qt");
        
        w.show();
    
        return a.exec();
    }
    
    
    // MainWindow.h
    
    
    #pragma once
    
    #include <QtWidgets/QMainWindow>
    #include "ui_MainWindow.h"
    #include <mgl2/mgl.h>
    #include <mgl2/qmathgl.h>
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = Q_NULLPTR);
        ~MainWindow();
        Ui::MainWindowClass ui;
    
    private:
        QString imagename;
        QMathGL* QMGL;  // 定义一个QMathGL对象
    
    private slots: //槽函数
        void on_pushButton_open_clicked();
        void on_pushButton_hist_clicked();
    
    public: // 静态变量、函数
        static double* d_hist;
        static int drawHist(mglGraph* gr);
    };
    
    // MainWindow.cpp
    
    
    #include "MainWindow.h"
    #include <QDir>
    #include <QtWidgets/QApplication>
    #include <QtWidgets/QScrollArea>
    #include <QFileDialog>
    #include "ui_MainWindow.h"
    #include <opencv2/opencv.hpp>
    #include <string>
    #include <mgl2/qmathgl.h>
    #include <mgl2/data.h>
    
    double* MainWindow::d_hist = NULL;
    
    // 构造函数
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        ui.setupUi(this);
        QMGL = new QMathGL(ui.widget_main); 
    }
    
    // 析构函数
    MainWindow::~MainWindow()
    {
        delete this->QMGL;
    }
    
    // static 绘图函数
    int MainWindow::drawHist(mglGraph* gr)
    {
        mglData y{ 256, d_hist }; // MathGL 专有的数据类型
        y.Norm((mreal)0,(mreal)1); // 归一化
        gr->SetRanges(0, 255, 0, 1); // 坐标范围
        gr->Axis(); // 坐标轴
        mglDataA* yA;
        yA = &y;
        gr->Bars(*yA);
        return 0;
    }
    
    // 选择文件
    void MainWindow::on_pushButton_open_clicked()
    {
        // 选择单个文件
        QString curPath = QDir::currentPath();
        QString dlgTitle = "选择一张图片:";
        QString filter = "Image(*.jpg)";
        QString aFileName = QFileDialog::getOpenFileName((QWidget*)this, dlgTitle, curPath, filter);
    
        if (!aFileName.isEmpty())
        {
            this->imagename = aFileName;
            this->ui.lineEdit_image->setText(imagename);
        }
    }
    
    // 直方图
    void MainWindow::on_pushButton_hist_clicked()
    {
        if (this->imagename.isEmpty())
        {
            return;
        }
    
        cv::Mat image = cv::imread((this->imagename).toStdString());
    
        int channels = 1;
        int histsize = 256;
        float range[] = { 0,255 };
        const float* histRanges = { range };
        cv::Mat mHist;
        cv::calcHist(&image, 1, &channels, cv::Mat(), mHist, 1, &histsize, &histRanges, true, false);
        
        d_hist = new double[256];
        for (int i = 0; i < 256; ++i)
        {
            d_hist[i] = mHist.at<float>(i);
        }
       
        // 绘图
        this->QMGL->setDraw(drawHist);
        this->QMGL->adjust();
        this->QMGL->setZoom(true);
        this->QMGL->setRotate(true);
        this->QMGL->update();
    }
    
  4. 结果

    在这里插入图片描述

    在这里插入图片描述

  5. 验证

    以Lena图为例,本程序绘制的直方图与ImageJ相同。以下为ImageJ的结果。

    在这里插入图片描述

参考资料

  1. https://blog.csdn.net/vaincury/article/details/107248421
  2. https://blog.csdn.net/vaincury/article/details/105438971
  3. https://www.bilibili.com/video/BV11C4y1s7Jp?from=search&seid=7524128681462799090
  4. https://www.bilibili.com/video/BV1yQ4y1K79n?from=search&seid=7524128681462799090
  5. https://www.bilibili.com/video/BV1q4411r7tM?from=search&seid=7524128681462799090
欢迎交流
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值