VS下呼叫Matlab引擎
VS2008是當前主流的應用程式開發環境之一,開發環境強大,開發的程式執行速度快。但在科學計算方面函式庫顯得不夠豐富、讀取、顯示資料圖形不方便。Matlab是一款將數值分析、矩陣計算、訊號處理和圖形顯示結合在一起,包含大量高度整合的函式可供呼叫,適合科學研究、工程設計等眾多學科領域使用的一種簡潔、高效的程式設計工具。不過由於Matlab使用的是解釋性語言,大大限制了它的執行速度和應用場合。基於VC和Matlab混合程式設計是很多熟悉VC++程式設計而又需要進行科學計算、資料模擬的科研人員常用的一種方式,其中最簡單也最直接的方法就是呼叫Matlab引擎。本文以下部分將詳細介紹通過VS2008呼叫Matlab2007a引擎來達到VC與Matlab資料共享程式設計的方法。 1. 什麼是Matlab引擎 所謂Matlab引擎(engine),是指一組Matlab提供的介面函式,支援C/C++、Fortran等語言,通過這些介面函式,使用者可以在其它程式設計環境中實現對Matlab的控制。可以主要功能有: ★ 開啟/關閉一個Matlab對話; ★ 向Matlab環境傳送命令字串; ★ 從Matlab環境中讀取資料; ★ 向Matlab環境中寫入資料。 與其它各種介面相比,引擎所提供的Matlab功能支援是最全面的。通過引擎方式,應用程式會開啟一個新的Matlab程序,可以控制它完成任何計算和繪圖操作。對所有的資料結構提供100%的支援。同時,引擎方式開啟的Matlab程序會在工作列顯示自己的圖示,開啟該視窗,可以觀察主程式通過engine方式控制Matlab執行的流程,並可在其中輸入任何Matlab命令。 實際上,通過引擎方式建立的對話,是將Matlab以ActiveX控制元件方式啟動的。在Matlab初次安裝時,會自動執行一次: matlab /regserver 將自己在系統的控制元件庫中註冊。如果因為特殊原因,無法開啟Matlab引擎,可以在Dos命令提示符後執行上述命令,重新註冊。 2. 配置編譯器 要在VC中成功編譯Matlab引擎程式,必須包含引擎標頭檔案engine.h並引入Matlab對應的庫檔案libmx.lib、libmat.lib、libeng.lib。具體的說,開啟一個工程後,做如下設定: 1) 通過選單工具/選項,開啟選項頁,點選專案和解決方案,然後在頁面右面“顯示以下內容的目錄”下拉列表框中選擇“包含檔案”,新增路徑:"D:\Program files\MATLAB\R2007a\extern\include"。 2) 選擇“庫檔案”,新增路徑:d:\Program Files\MATLAB\R2007a\extern\lib\win32\microsoft。 3) 右擊工程/屬性,開啟專案屬性頁,選擇連結器/輸入,在附加依賴項編輯框中,新增檔名libmx.lib libmat.lib libeng.lib。 以上步驟1)、2)只需設定一次,而步驟3)對每個專案都要單獨設定。 3. 引擎API詳解 在呼叫Matlab引擎之前,首先應在相關檔案中加入一行:#include "enging.h",該檔案包含了引擎API函式的說明和所需資料結構的定義。可以在VC中呼叫的引擎函式分別如下: 3.1 引擎的開啟和關閉 engOpen-開啟Matlab engine 函式宣告: Engine *engOpen(const char *startcmd); 引數startcmd是用來啟動Matlab引擎的字串引數,在Windows作業系統中只能為NULL。 函式返回值是一個Engine型別的指標,它是在engine.h中定義的engine資料結構。 EngClose-關閉Matlab 引擎 函式宣告: int engClose(Engine *ep); 引數ep代表要被關閉的引擎指標。 函式返回值為0表示關閉成功,返回1表示發生錯誤。 例如,通常用來開啟/關閉Matlab引擎的程式碼如下: Engine *ep; //定義Matlab引擎指標。 if (!(ep=engOpen(NULL))) //測試是否啟動Matlab引擎成功。 { MessageBox("Can't start Matlab engine!" ); exit(1); } . ………… engClose(ep); //關閉Matlab引擎。 3.2 向Matlab傳送命令字串 engString-傳送命令讓Matlab執行。 函式宣告: int engString(Engine *ep, Const char *string); 引數ep為函式engOpen返回的引擎指標,字串string為要matlab執行的命令。 函式返回值為0表示成功執行,返回1說明執行失敗(如命令不能被Matlab正確解釋或Matlab引擎已經關閉了)。 3.3 獲取Matlab命令視窗的輸出 要在VC中獲得函式engString傳送的命令字串被Matlab執行後在matlab視窗中的輸出,可以呼叫engOUtputBuffer函式。 函式宣告: int engOutputBuffer(Engine *ep, char *p, int n); 引數ep為Matlab引擎指標,p為用來儲存輸出結構的緩衝區,n為最大儲存的字元個數,通常就是緩衝區p的大小。該函式執行後,接下來的engString函式所引起的命令列輸出結果會在緩衝區p中儲存。如果要停止儲存,只需呼叫程式碼:engOutputBuffer(ep, NULL, 0)。 3.4 讀寫Matlab資料 3.4.1從Matlab引擎工作空間中獲取變數。 mxArray *engGetVariable(Engine *ep, const char *name); 引數ep為開啟的Matlab引擎指標,name為以字串形式指定的陣列名。 函式返回值是指向name陣列的指標,型別為mxArray*(mxArray資料型別在本文第4節詳細簡介)。 3.4.2 向Matlab引擎工作空間寫入變數。 int engPutVariable(Engine *ep, const char *name, const mxArray *mp); 引數ep為開啟的Matlab引擎指標,mp為指向被寫入變數的指標,name為變數寫入後在Matlab引擎工作空間中的變數名。 函式返回值為0表示寫入變數成功,返回值為1表示發生錯誤。 3.5 呼叫引擎時顯示/隱藏Matlab主視窗 預設情況下,以engine方式呼叫Matlab的時候,會開啟Matlab主視窗,可在其中隨意操作。但有時也會干擾應用程式的執行,可用以下設定是否顯示該視窗。 int engSetVisible(Engine *ep, bool value); 引數ep為開啟的Matlab引擎指標,value為是否顯示的標誌,取值true(或1)表示顯示Matlab視窗,取值false(或0)表示隱藏Matlab視窗。 函式返回值為0表示設定成功,為1表示有錯誤發生。 要獲得當前Matlab視窗的顯示/隱藏情況,可以呼叫函式: int engGetVisible(Engine *ep, bool *value); 引數ep為開啟的Matlab引擎指標,Value為用來儲存顯示/隱藏情況的變數(採用指標方式傳遞)。 函式返回值為0表示獲取成功,為1表示有錯誤發生。 4. 資料型別mxArray的操作 在上節的Matlab引擎函式中,所有與變數有關的資料型別都是mxArray型別。資料結構mxArray以及大量的mx開頭的函式,廣泛用於Matlab 引擎程式和Matlab C數學庫中。mxArray是一種很複雜的資料結構,與Matlab中的array相對應,我們只需熟悉Matlab的array型別和幾個常用的mxArray函式即可。 在VC中,所有和Matlab的資料互動都是通過mxArray來實現的,在使用mxArray型別的程式中,應包含標頭檔案matrix.h,不過在引擎程式中,一般會包含標頭檔案engine.h,該檔案裡面已經包含了matrix.h,因此無需重複包含。 4.1 建立和清除mxArray型資料 Matlab有很多種變數型別,對應於每種型別,基本上都有一個函式用於建立,但它們都有相同的資料結構,就是mxArray。 陣列的建立採用mxCreatexxx形式的函式,例如新建一個double型別陣列,可用函式mxCreateDoubleMatrix,函式形式如下: mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag); 引數m和n為矩陣的函式和列數。ComplexFlag為常數,用來區分矩陣中元素是實數還是複數,取值分別為mxREAL和mxCOMPLEX。 例如,建立一個3行5列的二維實數陣列,可用如下語句: mxArray *T = mxCreateDoubleMatrix(3, 5, mxREAL); 對應的,要刪除一個數組mxDestroyArray,該函式宣告如下: void mxDestroyArray(mxArray *array_ptr); 引數array_ptr為要刪除的陣列指標。 例如,要刪除上面建立的陣列T,可用如下語句: mxDestroyArray(T); 類似的建立函式還有: mxArray *mxCreateString(const char *str); 建立一個字串型別並初始化為str字串。 一般的在VC與Matlab互動中,以上兩種型別就夠了,其它型別陣列的建立這裡不再介紹。 4.2 管理mxArray資料型別 4.2.1 管理mxArray資料大小 要獲得mxArray陣列每一維上元素的個數,可以用mxGetM和mxGetN函式。其中mxGetM用來獲得陣列第一維的元素個數,對於矩陣來說就是行數。 int mxGetM(const mxArray *array_ptr); //返回array_ptr對應陣列第一維的元素個數(行數) int mxGetN(const mxArray *array_ptr); //返回array_ptr對應陣列其它維的元素個數,對於矩陣來說是列數。對於多維陣列來說是從第2維到最後一維的各維元素個數的乘積。 要獲得某一特定維的元素個數,則要用函式: const int *mxGetDimensions(const mxArray *array_ptr); 該函式返回array_ptr各維的元素個數儲存在一個int陣列中返回。對於常用的矩陣來說,用mxGetM和mxGetN兩個函式就可以了。 另外還可以通過mxGetNumberOfDimensions來獲得陣列的總的維數,用mxSetM、mxSetN設定矩陣的行數和列數,函式說明如下:
mxGetNumberOfDimensions(const mxArray *array_ptr); //返回陣列的維數 void mxSetM(mxArray *array_ptr, int m); //設定陣列為m行 void mxSetN(mxArray *array_ptr, int n); //設定陣列為n列
4.2.2 判斷mxArray陣列型別 在對mxArray型別的變數進行操作之前,可以驗證以下其中的陣列的資料型別,比如是否為double陣列、整數、字串、邏輯值等,以及是否為某種結構、類、或者是特殊型別,比如是否為空陣列,是否為inf、NaN等。常見的判斷函式有:
bool mxIsDouble(const mxArray *array_ptr); bool mxIsComplex(const mxArray *array_ptr); bool mxIsChar(const mxArray *array_ptr); bool mxIsEmpty(const mxArray *array_ptr); bool mxIsInf(double value); …… ……
這些函式比較簡單,意義自明,不再解釋。 4.2.3 管理mxArray陣列的資料 對於常用的double型別的陣列,可以用mxGetPr和mxGetPi兩個函式分別獲得其實部和虛部的資料指標,這兩個函式的宣告如下:
double *mxGetPr(const mxArray *array_ptr); //返回陣列array_ptr的實部指標 double *mxGetPi(const mxArray *array_ptr); //返回陣列array_ptr的虛部指標
這樣,就可以通過獲得的指標對mxArray型別的陣列中的資料進行讀寫操作。例如可以用函式engGetVariable從Matlab工作空間讀入mxArray型別的陣列,然後用mxGetPr和mxGetPi獲得資料指標,對並其中的資料進行處理,最後呼叫engPutVariable函式將修改後的陣列重新寫入到Matlab工作空間。具體實現見第5節程式例項。
5. 程式例項 對大部分軟體研發人員來說利用VC程式設計方便、高效,但是要顯示資料圖形就不那麼容易了,這時候不防藉助Matlab引擎輔助畫圖做資料分析。下面通過例項演示如何利用VC呼叫Matlab繪圖,程式的主要功能是在VC中對陣列x計算函式值y=sin(x) ±log(x),然後呼叫Matlab繪製y對x的圖形。 在VC中新建工程,編寫程式碼如下:
#include <cstdio>
#include <iostream> #include <math.h> #include "engine.h" using namespace std; void main() { const int N = 50; double x[N],y[N]; int j = 1; for (int i=0; i<N; i++) //計算陣列x和y { x[i] = (i+1); y[i] = sin(x[i]) + j * log(x[i]);
j *= -1; } Engine *ep; //定義Matlab引擎指標。 if (!(ep=engOpen(NULL))) //測試是否啟動Matlab引擎成功。 { cout <<"Can't start Matlab engine!" <<endl; exit(1); }
//定義mxArray,為行,N列的實數陣列。 mxArray *xx = mxCreateDoubleMatrix(1,N, mxREAL); mxArray *yy = mxCreateDoubleMatrix(1,N, mxREAL); //同上。
memcpy(mxGetPr(xx), x, N*sizeof(double)); //將陣列x複製到mxarray陣列xx中。 memcpy(mxGetPr(yy), y, N*sizeof(double)); //將陣列x複製到mxarray陣列yy中。
engPutVariable(ep, "xx",xx); //將mxArray陣列xx寫入到Matlab工作空間,命名為xx。 engPutVariable(ep, "yy",yy); //將mxArray陣列yy寫入到Matlab工作空間,命名為yy。
//向Matlab引擎傳送畫圖命令。plot為Matlab的畫圖函式,參見Matlab相關文件。 engString(ep, "plot(xx, yy); "); mxDestroyArray(xx); //銷燬mxArray陣列xx和yy。 mxDestroyArray(yy);
cout <<"Press any key to exit!" <<endl; cin.get(); engClose(ep); //關閉Matlab引擎。 }
另外一個例子:
#include "engine.h" #include <cstdlib> #include <cstring> #include<stdio.h> //#include "matlab.h" using namespace std; int main() { { Engine *ep; if(!(ep=engOpen("\0"))) //開啟Matlab引擎,建立與本地Matlab的連線 { fprintf(stderr,"\n Can't start MATLAB engine\n"); exit(-1); } static double Areal[6]={1,2,3,4,5,6};
mxArray *T=NULL,*a=NULL,*d=NULL;
double time[10]={0,1,2,3,4,5,6,7,8,9};
T=mxCreateDoubleMatrix(1,10,mxREAL);
memcpy((char*)mxGetPr(T),(char*)time,10*sizeof(double));
engPutVariable(ep,"T",T);
eng_rString(ep,"D=.5.*(-9.8).*T.^5;");
eng_rString(ep,"plot(T,D);"); return 0; } }
6. 小結 本文詳細的介紹了Matlab引擎使用方法並演示了一個簡單的利用VC呼叫Matlab畫圖的程式例項。大多數時候,程式設計師可以利用Matlab強大的資料讀寫、顯示能力和VC程式設計的高效率。例如,在Matlab中要讀入一幅任意格式的影象均只需一條命令i=imread('test.jp');影象資料矩陣便存放在了二維陣列i中,可以通過VC讀入該陣列進行相關處理再呼叫Matlab顯示,這種混合程式設計方式能大大提高工作效率。 當然,利用VC編譯的Matlab引擎程式,執行環境中還必須Matlab的支援,如果要編譯完全脫離Matlab的程式,可採用其它方式,如利用第三方Matcom程式編譯獨立的可執行程式等