QT與MatLab混編

2020-08-11 22:26:00
該篇介紹QT下呼叫MatLab編譯好的執行庫
環境:Windows10,QT5.12.6,MatLab r2018b 64位元
MatLab的安裝可以看這篇文章MatLab安裝
一、開啓MatLab,先設定編譯器,在命令列輸入:

>> mbuild -setup C++

結果如下:
MBUILD configured to use 'Microsoft Visual C++ 2017' for C++ language compilation.
即設定C++編譯器,如果設定C編譯器可以輸入 mbuild -setup
二、編譯.m檔案,命令列輸入>> deploytool ,出現下圖結果:

在这里插入图片描述

選擇Library Complier,設定如下:

在这里插入图片描述

等待打包…
打包完成,輸出如下:

在这里插入图片描述

for_redistribution目錄下是MyAppInstaller_web.exe,這個是MATLAB執行時庫的和本專案的安裝檔案,執行後可從網上下載MATLAB的執行時庫進行安裝,還會安裝本專案生成的dll、lib和h檔案。

for_redistribution_files_only目錄下是編譯生成的.dll 、.lib和.h檔案,其中.lib和.h檔案是在Qt專案編譯時需要用到的,.dll檔案是程式執行時需要用到的。

for_testing  目錄下是用於測試的。

該段引自:https://blog.csdn.net/HongAndYi/article/details/79433623
三、在QT中設定專案需要的動態庫
右擊專案:

在这里插入图片描述

選擇Add Library:

在这里插入图片描述

先在QT專案下,新建include資料夾,把MatLab編譯好的.lib和.h複製過來:

在这里插入图片描述

而.dll檔案要放在專案生成的debug目錄下:

在这里插入图片描述

然後新增lib檔案,路徑是剛纔建立在QT專案下的include下的lib:

在这里插入图片描述

之後會在QT專案的.pro檔案生成如下程式碼:
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/include/ -lpso
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/include/ -lpso
else:unix: LIBS += -L$$PWD/include/ -lpso

INCLUDEPATH += $$PWD/include
DEPENDPATH += $$PWD/include

目前是把Matlab編譯好的庫檔案包含進來的,但是還沒完,還需要把MatLab需要的執行庫包含進來:
INCLUDEPATH += $$quote(C:/Program Files/MATLAB/R2018b/extern/include)
INCLUDEPATH += $$quote(C:/Program Files/MATLAB/R2018b/extern/include/win64)

INCLUDEPATH += $$quote(C:/Program Files/MATLAB/R2018b/extern/lib/win64/microsoft)
DEPENDPATH += $$quote(C:/Program Files/MATLAB/R2018b/extern/lib/win64/microsoft)

LIBS += -L$$quote(C:/Program Files/MATLAB/R2018b/extern/lib/win64/microsoft/) -llibmex
LIBS += -L$$quote(C:/Program Files/MATLAB/R2018b/extern/lib/win64/microsoft/) -llibmx
LIBS += -L$$quote(C:/Program Files/MATLAB/R2018b/extern/lib/win64/microsoft/) -llibmat
LIBS += -L$$quote(C:/Program Files/MATLAB/R2018b/extern/lib/win64/microsoft/) -llibeng
LIBS += -L$$quote(C:/Program Files/MATLAB/R2018b/extern/lib/win64/microsoft/) -lmclmcr
LIBS += -L$$quote(C:/Program Files/MATLAB/R2018b/extern/lib/win64/microsoft/) -lmclmcrrt
之所以加quote是因爲路徑中包含空格,比如Program Files,這種情況就需要加quote包含進來,否則會出錯。
四、在QT專案下呼叫MatLab編譯好的pso.dll中的函數,先說一下QT中的編譯器,我的有MinGW也有MSVC編譯器,我用MinGW測試的時候會出錯,程式無法執行,但是用MSVC編譯器就沒有問題,所以建議MatLab的c++編譯器和QT編譯器要版本一致,

在这里插入图片描述

我的MSVC編譯器是手動設定的,因爲之前裝過vs2017,所以設定起來很方便,只需要安裝debugger就可以,CDB安裝可以在官網下載Debugging Tools for Windows 10 (WinDbg) ,安裝操作很簡單。

在这里插入图片描述

我的呼叫程式碼如下:
#include "pso.h"
//下面 下麪是呼叫MatLab編譯好的粒子羣演算法
    QLibrary myLib("pso.dll");
    typedef bool MW_CALL_CONV(*Fun)(int,class mwArray const &,class mwArray const &,class mwArray const &,class mwArray const &,class mwArray const &,class mwArray const &);
    Fun myFunc = Fun(myLib.resolve("?PSO@@YAXHAEAVmwArray@@000AEBV1@1@Z"));

    if(!psoInitialize())                                  //必須要初始化成功
    {
        qDebug()<<"could not initialize psodll\n";
        exit(0);
    }

    mwArray coor(str.size(),4,mxDOUBLE_CLASS);            //輸入值
    mwArray v(1,1,mxDOUBLE_CLASS);

    mwArray x(1,1,mxDOUBLE_CLASS);                        //輸出值
    mwArray y(1,1,mxDOUBLE_CLASS);
    mwArray z(1,1,mxDOUBLE_CLASS);
    mwArray t(1,1,mxDOUBLE_CLASS);

   
    coor.SetData(adjustToArray,4*str.size());             
    v(1,1)=WAVEVELOCITY;

    myFunc(4,x,y,z,t,coor,v);

    XRESULT=x.ToString();
    YRESULT=y.ToString();
    ZRESULT=z.ToString();
    TRESULT=t.ToString();
注:
需要說明的是由於matlab使用vs的msvc編譯器生成的dll檔案,生成後我們使用mingW呼叫,但生成的標頭檔案中指定各種編譯器對應的情況,但唯獨沒有MinGW,這就導致了在使用MinGW編譯器時我們在.pro檔案中新增:

DEFINES += MW_STDINT_H

使用msvc編譯器呼叫matlab生成的dll時,編譯器會自動識別函數名,因此也不需要使用resolve函數。

這段話引自:https://blog.csdn.net/China_Rocky/article/details/104592488
雖然我用的MSVC編譯器,但我還是呼叫了resolve函數來確定函數入口,不用的話會出錯,不清楚是什麼原因。
Fun myFunc = Fun(myLib.resolve("?PSO@@YAXHAEAVmwArray@@000AEBV1@1@Z"));
上述resolve中的值來自pso.dll(即剛纔matlab編譯好的執行庫),我們可以利用Dependency Walker 2.2該軟體檢視dll檔案,然後找到入口函數,後邊跟着的值就是這個resolve中的值。可以在matlab編譯生成的.h檔案中檢視專案生成的呼叫函數,比如:
extern LIB_pso_CPP_API void MW_CALL_CONV PSO(int nargout, mwArray& x, mwArray& y, mwArray& z, mwArray& t, const mwArray& sample, const mwArray& v);
說明:
1)DLL的初始化

在使用pso.dll裡的函數之前,必須先初始化。psoInitialize()是matAdd.h檔案裏面的庫初始化函數。

(2)PSO函數的輸入輸出參數

mwArray是MATLAB的陣列類,MATLAB編譯生成的DLL的介面函數的參數都是採用mwArray型別,例如:extern LIB_pso_CPP_API void MW_CALL_CONV PSO(int nargout, mwArray& x, mwArray& y, mwArray& z, mwArray& t, const mwArray& sample, const mwArray& v); 

int  nargout 是輸出參數個數,比如我這裏是4,那麼,後面x,y,z,t變數是輸出參數,
mwArray  &sample,  const mwArray  &v是輸入參數

(3)mwArray類的使用

mwArray coor(str.size(),4,mxDOUBLE_CLASS);  
定義mwArray型別的變數coor時,利用建構函式傳遞了陣列的行數、列數、元素型別、實數或複數型別語句

coor.SetData(adjustToArray,4*str.size());  將一維向量adjustToArray的數據內容賦值給coor,元素個數爲4*str.size(),等於行數*列數。使用SetData賦值時,輸入adjustToArray必須是一個向量,是逐列儲存的。

注意:mwArray陣列的下標都是從1開始的,與C/C++的陣列元素下標從0開始不同。

這段話修改自:https://blog.csdn.net/HongAndYi/article/details/79433623
另外mwArray的詳細用法如下:
.矩陣賦值
<1>mwArray 定義矩陣變數

  mwArray  A(rows, cols, type)

參數說明:
   A   :變數名
   rows:行數
   col :列數
   type:數t據型別

type型別有:
typedef enum
{
    mxUNKNOWN_CLASS = 0, //未知型別
    mxCELL_CLASS, //細胞型別
    mxSTRUCT_CLASS, //結構型別
    mxLOGICAL_CLASS, //布爾型別
    mxCHAR_CLASS,  //字串型別
    mxVOID_CLASS,  //void型別
    mxDOUBLE_CLASS, 
    mxSINGLE_CLASS, //單精度浮點數
    mxINT8_CLASS, //
    mxUINT8_CLASS,
    mxINT16_CLASS,
    mxUINT16_CLASS,
    mxINT32_CLASS,
    mxUINT32_CLASS,
    mxINT64_CLASS,
    mxUINT64_CLASS,
    mxFUNCTION_CLASS, //函數型別
    mxOPAQUE_CLASS, //
    mxOBJECT_CLASS  //物件型別
}

整體含義是:定義矩陣A,行數爲:rows,列數爲:cols,型別爲:type
注: 如果參數不是矩陣,只是一個數,令 rows=1,cols=1即可。

<2>矩陣賦初值:

int a[6] = {1,2,3,4,5,6}
mwArray A(2,3,mxINT32_CLASS);  
A.SetData(a,6); //第二個參數爲要設定的數的個數,大小可設爲rows*cols

注:該過程相當於把1*6的矩陣,轉化爲2*3的矩陣,matlab轉化順序是,先排第一列,由上到下爲a[0] a[1],然後排第二列,由上到下爲a[2] a[3],即轉化後的A爲:
1     3      5
2     4      6
如果要使A爲:
1      2     3
4      5     6
需這樣賦值:
<pre name="code" class="cpp">int a[6] = {1,2,3,4,5,6}
mwArray A(3,2,mxINT32_CLASS);  //修改此處:行列數互換
A.SetData(a,6); //第二個參數爲要設定的數的個數,大小可設爲rows*cols

此時生成的A爲:
1      4
2      5
3      6
該矩陣轉置之後,既可以達到所需形式,轉置過程可以在matlab的.m檔案中新增,先修改.m,然後在生成dll、lib、h檔案。
尤其是,在影象處理時,如果傳遞的矩陣爲影象數據矩陣,要採用後一種方法賦值,否則,影象會嚴重變形失真。
二、字串賦值
char str[5] = "abcd";
//或 CString str = "abcd"
mwArray mwA(str);

該段轉載自:https://blog.csdn.net/hong__fang/article/details/43307701
僅用於學習使用