以前在運行別人論文的代碼的時候,常常有遇到Matlab與C++混合編程的影子。實際上就是經過Matlab的Mex工具將C++的代碼編譯成 Matlab支持調用的可執行文件和函數接口。這樣一方面能夠在Matlab中利用已經編寫好的函數,儘管這個函數是用C++編寫的。實現了交流無國界, 沒有江山一統的誰,只有四海以內皆兄弟的豪氣。另外一方面,取C++所長補己之短。Matlab擅長矩陣運算,但對循環操做的效率不及C++來得高效,例如 Hilbert矩陣的建立。因此對於具備大循環的運算,能夠借C++之力來完成。html
看到它的魅力,以前也一直想學下,惋惜機緣不對。但在昨天緣分就到了。我須要用到一個論文給出來的代碼,可是它的代碼是C++的,並且還依賴了 OpenCV的庫,基於Linux平臺。這與實驗室給我定出來的平臺有很大的不一樣,咱們是得統一基於Windows + Matlab來實現的,這樣組內各個同窗的工做纔好統一。因此沒辦法了,就得把這個原做者的代碼編譯成Matlab支持的可執行文件。ios
1、初級c++
在使用MATLAB編譯C/C++代碼時,咱們須要修改C/C++代碼,在裏面添加Matlab能支持的函數接口。這樣Matlab才能調用它。而後再經過Matlab的Mex工具來編譯它。下面就具體的舉例子說明這兩個步驟。編程
假設咱們有一個很簡單的C++代碼,實現的就是兩個double型數的加法:數組
mexAdd.cppide
一、修改代碼文件函數
1)添加頭文件mex.h工具
在咱們的c++文件開頭處添加頭文件:測試
#include"mex.h"優化
2)添加接口函數mexFunction()
mexFunction的定義爲:
void mexFunction(int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[])
{
}
首先,這個函數是沒有返回值的。它不是經過返回值把c++代碼的計算結果傳回Matlab的,而是經過對參數plhs的賦值。例如咱們在Matlab中,調用這個add函數通常是這樣:
>> a = 0.5; b = 0.8;
>> c = add(a, b);
那mexFunction怎麼將輸入參數a和b傳入給c++的add函數,而後就怎麼把計算結果返回給c呢?這些粗重活所有經過mexFunction的四個參數來實現:
nlhs: 感受是number of left hand size parameters,也就是Matlab調用語句左邊的變量個數,實際上就是須要返回給Matlab的返回值變量有多少個。例如上面c = add(a, b);就只有一個返回參數c,因此nlhs就是1;
plhs: 感受是pointer of left hand size parameters,也就是函數返回參數的指針。但它是一個指針數組。換句話說,它是一個數組,每一個元素是個指針,每一個指針指向一個數據類型爲 mxArray的返回參數。例如上面c = add(a, b);就只有一個返回參數c,因此該數組只有一個指針,plhs[0]指向的結果會賦值給c。
nrhs: 這個是number of right hand size parameters,也就是Matlab調用語句右邊的變量個數。例如上面c = add(a, b),它給c++代碼傳入了兩個參數a和b,因此nrhs爲2;
prhs:這個是pointer of right hand size parameters,和plhs相似,由於右手面有兩個自變量,即該數組有兩個指針,prhs[0]指向了a,prhs[1]指向了b。要注意prhs 是const的指針數組,即不能改變其指向內容。
由於Matlab最基本的單元爲array,不管是什麼類型也好,若有doublearray、 cell array、struct array……因此a,b,c都是array,b = 1.1即是一個1x1的double array。而在C語言中,Matlab的array使用mxArray類型來表示。因此就不難明白爲何plhs和prhs都是指向mxArray類型 的指針數組(參考資料[1])。
那mexFunction函數的函數體要怎麼寫呢?怎麼樣經過這個接口函數將Matlab的參數和c++代碼中的相對應的參數聯繫起來呢?咱們先把這個代碼所有展示出來。
最後的mexAdd.cpp是這樣:
mexAdd.cpp
mexFunction的內容是什麼意思呢?咱們知道,若是在Matlab中這樣調用函數時:
>> output = add(0.5, 0.8);
在未涉及具體的計算時,output的值是未知的,是未賦值的。因此在具體的程序中,咱們創建一個1x1的實double矩陣(使用 mxCreateDoubleMatrix函數,其返回指向剛創建的mxArray的指針),而後令plhs[0]指向它。接着令指針a指向plhs [0]所指向的mxArray的第一個元素(使用mxGetPr函數,返回指向mxArray的首元素的指針)。一樣地,咱們把prhs[0]和prhs [1]所指向的元素(即0.5和0.8)取出來賦給b和c。因而咱們能夠把b和c做自變量傳給函數add,得出給果賦給指針a所指向的mxArray中的 元素。由於a是指向plhs[0]所指向的mxArray的元素,因此最後做輸出時,plhs[0]所指向的mxArray賦值給output,則 output即是已計算好的結果了。
實際上mexFunction是沒有這麼簡單的,咱們要對用戶的輸入自變量的個數和類型進行測試,以確保輸入正確。如在add函數的例子中,用戶輸入char array即是一種錯誤了。
從上面的講述中咱們總結出,MEX文件實現了一種接口,把C語言中的計算結果適當地返回給Matlab罷了。當咱們已經有用C編寫的大型程序時,大可不 必在 Matlab裏重寫,只寫個接口,作成MEX文件就成了。另外,在Matlab程序中的部分計算瓶頸(如循環),可經過MEX文件用C語言實現,以提升計 算速度(參考資料[1])。
二、編譯修改後的c++文件
文件修改完後,咱們須要將他編譯,生成Matlab支持的可執行文件。這裏須要的是Matlab自帶的Mex工具。但在編譯器,咱們須要配置下這個工具,告訴它你要採用什麼編譯器來編譯咱們的c/c++代碼。在Matlab中運行:
>> mex -setup
就會出現叫你選擇一個默認的編譯器。例如我這裏是叫選擇Matlab自帶的Lcc或者我本身在電腦上安裝的Microsoft Visual C++ 2010。通常都是選擇後者。配置這個就能夠編譯了。編譯也有如下幾種狀況:
>> mex XXX.cpp
>> mex X1.cpp X2.cpp X3.cpp %多個cpp文件,且有依賴。生成的庫名字叫X1
>> mex -O X1.cpp %大寫O選項,優化編譯
>> mex -largeArrayDims X1.cpp %對64位系統,經過這個選項來指定使用處理大容量數組的API。由於Matlab與C++之間的接口是以32位系統做爲標準的,這就致使了人們在處理大 容量數據時沒辦法利用C和C++語言的速度優點。但對64位系統來講,系統資源通常都比32位系統要充足,因此指定該接口,讓它對大容量數據處理更遊刃有 餘。
還有一些編譯選項,和gcc同樣。例如-I指定額外須要include的目錄,-L指定額外須要鏈接的庫的目錄,-l指定額外須要連接的庫等。
對於咱們的程序就簡單了。在MATLAB命令窗口輸入如下命令:mexmexAdd.cpp,便可編譯成功。編譯成功後,在同文件夾下會出現一個同名 的,但後綴是mexw32(32位的系統)或者mexw64(64位的系統)的文件,例如mexAdd.mexw32。而後在Matlab中就能夠直接調 用它來運算了:
>> ans = mexAdd(0.5, 0.8);
2、進階
上面咱們針對的是處理標量的狀況,也就是數a,b或者c。這節咱們讓它處理二維數組,也就是圖像。爲了驗證,咱們很傻瓜地完成如下功能:
>> [grayImage] =RGB2Gray('imageFile.jpeg');
也就是將一個圖像文件名,傳遞給c++的代碼,而後c++代碼將這個圖像讀入,再轉成灰度圖,而後返回給Matlab。而c++代碼裏面的圖像讀入和灰 度轉換的操做經過調用OpenCV的庫函數來實現。是否是很傻瓜呢?由於Matlab已經有實現一樣功能的函數了。對,沒錯,就是畫蛇添足。但咱們只是爲 了說明二維數組的傳遞過程,沒有什麼用意。不過,若是要計算兩個圖像的光流的話,Matlab可能就真正須要OpenCV的幫助了。
另外,由於cpp文件要連接OpenCV的庫,因此爲了統一或者規範編譯工程,我寫了一個make.m文件,它的功能相似於Makefile,實際上就實現了mex編譯這個工程時候的編譯規則。具體能夠看後面的代碼,而後就知道在裏面作了什麼了。
首先是RGB2Gray.cpp代碼:
和上面的相比,裏面多了幾個東西。第一個就是傳入參數的測試,看看Matlab傳入的參數是否存在錯誤,還包括了些異常處理。第二個就是幫助信息。第三 個就是主要的實現函數了。只有OpenCV的讀圖像和灰度轉換這裏就不講了,就是兩個函數的調用。關鍵的地方仍是若是把一個圖像,也就是二維數組,傳遞給 mexFunction的參數,讓它返回給Matlab。實際上,咱們只要清楚一點:
plhs[0] = mxCreateDoubleMatrix(2, 3,mxREAL);
這個函數創建的矩陣的指針plhs[0]是按照列的方式來存儲的。假設imgMat是它的指針,那麼*(imgMat+1)就是矩陣元素[1, 0],*(imgMat+2)就是矩陣元素[0, 1],*(imgMat+4)就是矩陣元素[0, 2]。上面的代碼就是按照這個方式,將圖像gray中像素值賦值給參數plhs[0]相應的位置(實際上也許能夠直接內存拷貝,但由於裏面是指針操做,涉 及到局部變量gray的銷燬問題,因此就簡單的用上面的笨但妥當的方式來實現了)。
好了,下面是make.m文件。裏面須要獲取你的電腦的系統版本是32仍是64位的,來選擇編譯選項。而後添加OpenCV的相關配置。若是您須要使用 使用,請修改爲您的OpenCV的相關目錄。而後給出一個須要編譯的文件的列表。最後分析這個列表,加上編譯選項,用mex來編譯列表裏面的全部文件。
3、使用方法和結果
一、編譯
直接在Matlab中運行make.m。便可生成RGB2Gray.mexw64。而後在Matlab中運行:
>> img = RGB2Gray(‘d:\test.jpg’);
>> imshow(uint8(img));
便可顯示轉換結果,如圖:
注:以上Matlab的說明都是在你的cpp文件所在目錄下。
4、參考資料
[1] 如何寫mexFunction函數