1. 程式人生 > >Windows 64 + Matlab 64 MEX混合程式設計初步

Windows 64 + Matlab 64 MEX混合程式設計初步

說明

記錄本人在Windows下,使用Matlab 64-bit 版進行混合程式設計時遇到的問題,主要記錄編譯器的安裝與簡要的混編過程。

  • Matlab版本:Matlab R2014a 64-bit、Matlab R2015b 64-bit
  • 作業系統版本:Windows 8.1 64-bit、Windows10 64-bit
  • 編譯器:Microsoft Windows SDK 7.1(C/C++) 和 GNU編譯器(有MinGW(Minimalist GNU for Windows,僅32位)、MinGW64(64位和32位)、TMD-GCC(非官方的編譯器包)、CygWin等等,它們間的區別與聯絡參見
    GCC_Cygwin_MinGW_TDM-GCC

Matlab混合程式設計

下載與安裝編譯器

Microsoft Windows SDK 7.1(C/C++)

下載完成後根據提示安裝即可,如果出現問題,安裝失敗,參考: 部分。

TDM-GCC(gcc/g++)

For Matlab 2015

  • 注意,MATLAB 2015現僅支援GCC4.9.2,在安裝TMD-GCC時,不要勾選更新,如下圖:
    安裝TMD-GCC注意事項

  • 然後,還需注意的是安裝路徑不能有空格;

  • 需要新增系統環境變數MW_MINGW64_LOC=your tdm-gcc path
  • 重啟系統和MATLAB後,輸入mex -setup
    顯示:
    MATLAB2015 MinGW64

至此完成!

For Matlab 2014

如下文章針對於MATLAB 2014a。

點此tmd-gcc開啟下載頁面,選擇64位版本,40M+,全部安裝佔用400M+磁碟空間。

根據以前使用經驗,安裝路徑最好不要有空格中文。

安裝完成後,開始 -> 執行 -> cmd開啟Windows命令視窗,輸入gcc -v,如下圖所示,顯示安裝資訊,說明安裝成功:
安裝成功

下面的程式碼是從Bogdan主頁上下載的mexopts.bat檔案程式碼,使用時需要做一點小小的改動。

在下面的程式碼中找到:set MINGWPATH=p:\mingw64這句程式碼,將其中的路徑p:\mingw64

替換成你的TMD-GCC-64的安裝目錄,如我的是:E:\devtools\TDM-GCC-64,然後儲存並將mexopts.bat檔案copy到計算機如下路徑(複製下面的路徑地址,貼上到資源管理器視窗位址列,回車即可):

%USERPROFILE%\AppData\Roaming\Mathworks\MATLAB\R2014a\

下面是程式碼:

@echo off

:: NOTE: this is actually not a proper .bat file executed by Windows. MEX
::       parses it and only understands a very reduced set of commands:
::       "set" and "rem" apparently, everything else is ignored (behaves as
::       "rem"), so don't do any fancy batch stuff in here. There are some
::       undocumented special vars you can set here that will trigger MEX
::       to do fancy stuff.

:: You can use MinGW64 builds (win32 threads + seh unwinding) from here:
:: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/

:: Tested with the following:
:: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.1/threads-win32/seh/x86_64-4.9.1-release-win32-seh-rt_v3-rev1.7z

:: Set this to your Mingw64 top folder, where you extracted the above
set MINGWPATH=p:\mingw64

:: Leave these alone unless you know what you're doing.
set PATH=%MINGWPATH%\bin;%PATH%
set PRELINK_CMDS=echo.>%TEMP%\mexstaticlibs

:: You can have MEX run some commands before calling the linker.
:: The two examples below will cause gcc to output the full path to some
:: static libraries so you can link statically to them (see the
:: LINGFLAGSPOST special var below). You can set any command here, however.
rem set PRELINK_CMDS1=gcc -print-file-name=libwinpthread.a >> %TEMP%\mexstaticlibs
rem set PRELINK_CMDS2=gcc -print-file-name=libquadmath.a >> %TEMP%\mexstaticlibs
rem set PRELINK_CMDS3=...

:: You can have MEX run some commands also after calling the linker
:: (e.g. upx compress the output .mex)
rem set POSTLINK_CMDS1=upx -9 "%OUTDIR%%MEX_NAME%%MEX_EXT%"
rem set POSTLINK_CMDS2=...

:: You can change these if you really need to.
set COMPILER=g++
set COMPFLAGS=-c -I"%MATLAB%\extern\include" -DMATLAB_MEX_FILE
set OPTIMFLAGS=-O3 -funroll-loops -DNDEBUG
set DEBUGFLAGS=-g
set NAME_OBJECT=-o

set LINKER=g++
set LINKFLAGS=-shared -static-libstdc++ -static-libgcc -L"%MATLAB%\bin\win64" -L"%MATLAB%\extern\lib\win64\microsoft" -lmex -lmx -leng -lmat -lmwlapack -lmwblas

set [email protected]%TEMP%\mexstaticlibs
set NAME_OUTPUT=-o "%OUTDIR%%MEX_NAME%%MEX_EXT%"


:: EXAMPLES
:: ========

:: You can compile simple files using "mex file.cpp". To support more than 2^32 elements, use
:: "mex -largeArrayDims file.cpp" ... use this by default, unless you know what you're doing.

:: To add include dirs, lib dirs, or compile/link flags, do:
:: mex COMPFLAGS="$COMPFLAGS -std=gnu99 -Ix:/include/dir" LINKFLAGS="$LINKFLAGS -Lx:/libs/dir -lmylib" -largeArrayDims file.cpp

選擇編譯器

  1. 檢視可用編譯器:使用mex -setup檢視可選擇的編譯器,如果沒有需要自己安裝,如果已安裝且只安裝了編譯器Microsoft Windows SDK 7.1(C/C++),顯示如下結果:
    檢視可用編譯器

  2. 選擇編譯器:使用mex -setup lang選擇編譯器,如mex -setup C++選擇C++編譯器,如下圖:
    選擇編譯器

  3. 如果你也按上面TDM-GCC(gcc/g++) 中講的方法,配置了TDM-GCC-64位的編譯器,那麼,你會發現結果如下:
    沒有顯示TDM-GCC-64編譯器
    剛開始我也以為不能用,後來試著編譯了一下,竟然可以!
    使用TDM-GCC-64編譯器編譯
    DT.c檔案中重複定義了無窮大,註釋掉,就沒有提示啦!

編寫c/cpp檔案

與普通的C檔案的編寫有兩點不同:

  1. 包含mex.h標頭檔案,即:#include "mex.h"

  2. 編寫mexFunction函式。

下面以一個求和的例子說明:

#include "mex.h" // 使用MEX檔案必須包含的標頭檔案
// 執行具體工作的C函式
double add(double x, double y)
{
    return x + y;
}
// MEX檔案介面函式
void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])
{
    double *z;
    double x, y;
    plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
    z = mxGetPr(plhs[0]);
    x = *(mxGetPr(prhs[0]));
    y = *(mxGetPr(prhs[1]));
    *z = add(x, y);
}

Matlab命令視窗輸入mex add.cpp,執行結果如下圖:
編譯結果

在Windows上編譯後生成mexw32mexw64檔案,在Linux上編譯會生成mexa32mexa64檔案。

mexFunction函式介紹

mexFunction函式介面如下:

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])

可見,是沒有返回值的,其實是通過指標陣列plhs傳遞的。它共有四個引數,即左邊的輸出引數及數目和右邊的輸入引數及數目,具體引數意義如下:

引數 意義 英文全稱 型別
nlhs 左邊輸出引數的數目 number of left-hand side 整型(int)
plhs 指向輸出引數的指標 pointer of left-hand side 指標陣列
nrhs 右邊輸入引數的數目 number of right-hand side 整型(int)
prhs 指向輸入引數的指標 pointer of right-hand side 指標陣列

詳情參考:

編譯多個c/cpp檔案

假設A.c呼叫B.cB.c呼叫C.c
可以使用如下命令編譯:

mex C.c
mex B.c
mex A.c

或者

mex A.c B.c C.c

一些例項

MATLAB2015編譯MeanShift

MeanShift分割原始碼包,可以從這裡下載,也可以從本人網盤(含MATLAB介面,和編譯好的mexw32mexw64mexa64)下載。

原本程式碼中沒有適用於Windows64和MATLAB64位的mexw64檔案,需要自己編譯生成,方法很簡單,執行資料夾中的compile_edison_wrapper.m檔案即可。

然後,讀入一幅影象,呼叫edison_wrapper程式實現分割,注意要使edison_wrapper資料夾處於當前路徑。如讀入一幅Lena RGB影象,分割結果如下:

I=imread('D:\DataSets\oi\nsi\classical\LenaRGB.bmp');
[fimage, labels] = edison_wrapper(I, @RGB2Luv);
figure
subplot(121)
imshow(I)
title('Orignal RGB image')
subplot(122)
imagesc(labels)
axis image % 座標軸與影象尺寸一致
title('Segmentation result:labels')

MeanShift分割

Problem&Solution

找不到編譯器或SDK

Problem

提示:“未找到支援的編譯器或 SDK”,如下圖:
未找到支援的編譯器

Solution

名稱 版本
GRMSDK_EN_DVD.iso x86
GRMSDKIAI_EN_DVD.iso Itanium
GRMSDKX_EN_DVD.iso amd64

注意

A problem occurred while installing selected Windows SDK components.

Installation of the “Microsoft Windows SDK for Windows 7” product has reported the following error: Please refer to Samples\Setup\HTML\ConfigDetails.htm document for further information.

Please attempt to resolve the problem and then start Windows SDK setup again. If you continue to have problems with this issue, please visit the SDK team support page at http://go.microsoft.com/fwlink/?LinkId=130245.

Click the View Log button to review the installation log.
To exit, click Finish.

如下圖:
A problem occurred while installing selected Windows SDK components.

解決辦法
出現此問題,很可能是你的PC機已經安裝了:Microsoft Visual C++ 2010,而在安裝Microsoft Windows SDK for Windows 7 and .NET Framework 4 (ISO)時預設選擇安裝Microsoft Visual C++ 2010,如下圖,造成的衝突,可以解除安裝已經安裝的,注意32和64位的都要解除安裝

Windows SDK 安裝選項

解除安裝完成後從新安裝即可!!!

error C2143: syntax error

Problem

提示錯誤:error C2143: syntax error : missing ‘;’ before ‘type’,如下圖所示:
錯誤提示

Solution

出現這種現象,很可能是你在用VC的編譯器編譯GNU的C語言寫的程式碼,兩個編譯器中的C標準不一樣。

一種方法是將檔案字尾名.c,改成.cpp,重新編譯,但可能還會有其它的錯誤。

如果原始檔是GNU的C程式碼,建議安裝相應的編譯器,然後編譯,可參考TDM-GCC(gcc/g++)

一些有用的總結

注意系統平臺和檔案路徑

MATLAB 匹配查詢檔案的方式是這樣的:

先從當前目錄尋找,假如沒有,再到搜尋路徑中從頂層尋找,如果在同一目錄下找到了函式名相同的.m檔案、.mex…檔案,如果.mex…檔案和平臺匹配,則執行之,反之執行.m檔案。

系統平臺

假如你有這樣一個工具包tools,檔案結構如下,tools為當前目錄
tools目錄結構示意

其中,func.m函式為MATLAB版,同時又用C語言寫了類似的函式,並mex混編生成了Linux平臺下的:mexa32、mexa64;Windows平臺下的:mexw32、mexw64以及MAC平臺下的:mexmac檔案,如上圖。

  • 假如你現在處於Windows系統,並且MATLAB版本為64位,那麼,你只能成功呼叫func.mfunc.mexw64兩個檔案,並且會優先選擇func.mexw64,而“mexw32”和“mexa64”都是不能用的。
  • 假如你現在處於Windows系統,並且MATLAB版本為32位,那麼,你只能成功呼叫func.mfunc.mexw32兩個檔案,並且會優先選擇func.mexw32,“mexw64”和“mexa32”都是不能用的。

檔案路徑

依然如上圖tools目錄結構示意所示,tools為當前目錄,不同的是:

  • 你刪除了func.mexw32func.mexw64檔案,你的執行環境是Windows下的MATLAB,那麼你呼叫成功的是func.m檔案,而不是“mex”資料夾中的func.mexw32func.mexw64,因為MATLAB第一匹配到“func”函式的是func.m

執行效率對比

實驗說明:

主要對比MATLAB與C的(mex函式)分別在32和64位MATLAB上的執行效率。

實驗環境:ThinkPad E430c + Windows 10 + MATLAB 2015a(32bit)/ MATLAB 2015b(64bit)。

  • 實驗1:迴圈N次,N=2×108

test.m 原始碼:

N = 2*10e8;
tic
func(N)
toc

tic
for i=0:N-1
end
toc

混編C程式碼(func.c):

#include "mex.h" // include head files
// function
double loopN(double N)
{
    int i = 0;
    for(i = 0; i< N; i++); 
    return i;
}
// interface
void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])
{
    double *z;
    double N;
    plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
    z = mxGetPr(plhs[0]);
    N = *(mxGetPr(prhs[0]));
    *z = loopN(N);
}
  • 實驗2:提取影象特徵,工具包 matlabPyrTools

測試MATLAB程式碼:

N = 20;
I=imread('D:\DataSets\oi\nsi\classical\BaboonRGB.bmp');
time_using = cputime;
grayimg = double(rgb2gray(uint8(I)));
for i = 1:N
    [pyr,pind] = buildSpyr(grayimg,3,'sp3Filters');
    pyramids = getSpyr(pyr,pind);
end
time_using = cputime - time_using;
time_using = time_using/N;
disp(time_using)
  • 實驗3:MeanShift分割,工具包 edison_wrapper,含MATLAB interface。

測試MATLAB程式碼:

N = 20;
I=imread('D:\DataSets\oi\nsi\classical\BaboonRGB.bmp');
time_using = cputime;
for i = 1:N
    [fimage, labels] = edison_wrapper(I, @RGB2Luv);
end
time_using = cputime - time_using;
time_using = time_using/N;
disp(time_using)
  • 實驗4:MeanShift分割,工具包 edison_wrapper,含MATLAB interface,對用matlabPyrTools提取的金字塔特徵進行分割。

測試MATLAB程式碼:

N = 20;
I=imread('D:\DataSets\oi\nsi\classical\BaboonRGB.bmp');
time_using = cputime;
for i = 1:N
    [fimage, labels] = edison_wrapper(I, @ExtractFeature,  'SpatialBandWidth', 7, 'RangeBandWidth', 12, 'MinimumRegionArea', 200);
end
time_using = cputime - time_using;
time_using = time_using/N;
disp(time_using)

實驗結果:

單位:s

實驗序號 MATLAB(32) MATLAB(64) C(MEX, 32) C(MEX, 64)
1 4.91 7.42 4.54 1.43
2 - - 0.9953 0.4453
3 - - 4.10 4.98
4 - - 11.64 17.63

實驗結果很不可思議!

  1. 實驗1:64位MATLABfor迴圈竟然比32位的慢了好些,而mex的C則相反(正常表現),不過可以看出C語言要比MATLAB快;
  2. 實驗2:正常表現;
  3. 實驗3:有些反常;
  4. 實驗4:64位竟然慢了那麼多!

對於實驗4,觀察返回值labels,即超畫素的標籤,發現32位和64位結果並不一致,但大致相同,圖形顯示如下:
labels結果(MATLAB 32位和64位)

MATLAB 32位和64位下得到的labels的部分值:
labels32和labels64部分值