《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 嵌入式技術(shù) > 設(shè)計應(yīng)用 > 基于COM的Matlab參數(shù)處理與圖像嵌入在VC中的實現(xiàn)
基于COM的Matlab參數(shù)處理與圖像嵌入在VC中的實現(xiàn)
來源:微型機與應(yīng)用2011年第24期
王 維, 劉 斌, 柳炳利, 魏友華
(成都理工大學(xué) 數(shù)學(xué)地質(zhì)四川省重點實驗室, 四川 成都 610059)
摘要: 通過運用Matlab與VC的語言和平臺特性,解決了二維數(shù)組參數(shù)的傳遞與輸出;介紹兩種Matlab圖像嵌入VC界面的方法,并以主成分分析為實例,講解了相關(guān)細節(jié)與難點。同時,介紹了一種更加靈活、簡便的讀取Excel文件的方法。
Abstract:
Key words :

摘  要:通過運用MatlabVC的語言和平臺特性,解決了二維數(shù)組參數(shù)的傳遞與輸出;介紹兩種Matlab圖像嵌入VC界面的方法,并以主成分分析為實例,講解了相關(guān)細節(jié)與難點。同時,介紹了一種更加靈活、簡便的讀取Excel文件的方法。
關(guān)鍵詞: COM組件; Matlab; VC; 二維數(shù)組; 圖像嵌入

    基于COM組件的Matlab與C++混合編程方式因擁有獨立的運行環(huán)境和兩種語言的互補優(yōu)勢而被眾多科研人員和編程人員所接受,同時也是MathWorks公司推薦使用的混編方式。但在程序設(shè)計過程中,通常會遇到兩個難點問題:(1)二維數(shù)組參數(shù)的傳遞與輸出; (2)Matlab生成圖形嵌入VC工程界面中。對于第一個問題,Bruce McKinney[1]在MSDN上指出;“如果對一維數(shù)組進行操作,則SAFEARRAY函數(shù)變的簡單且易操作。但是對于多維數(shù)組,同樣的操作要復(fù)雜得多”,造成這一問題的根源在于Matlab與C++對多維數(shù)組元素的存儲方式不同。而在本項目開發(fā)過程中回避了這個復(fù)雜的過程,轉(zhuǎn)而利用Matlab強大的數(shù)組處理功能將其解決。Matlab完善的圖形處理功能是其被科研人員所推崇的原因之一,但因其圖形擁有獨立的窗口,嚴(yán)重影響了整體界面美觀和用戶交互體驗,所以Matlab圖形嵌入是混合編程中不可回避的問題。對此將通過實例介紹兩種圖形嵌入方法并分析說明其優(yōu)缺點和相關(guān)細節(jié)。
    本文示例均在Matlab R2008a和VC 6.0平臺下完成,且通過調(diào)試能夠正常運行。
1 Excel文件讀取方法
    VC平臺下的Excel文件讀取方式復(fù)雜難懂,但如果使用Matlab與VC的混合編程方式編寫將更加簡便,而且擁有較強的可擴展性。下面代碼詳細講解了該過程。
    (1)在Matlab平臺下建立CExcelRead.m文件,代碼如下:
    function [output,row,col] = CExcelRead(filePath)
    //filePath包含了指定文件的路徑和文件名,由VC平臺函數(shù)GetPathName()獲取
    [typ, desc] = xlsfinfo(filePath);
    //xlsfinfo()可獲取指定Excel文件中各個sheet工作區(qū)的名稱,返回值存入名為desc的cell數(shù)組中
    sheet1 = cell2mat(desc(1));
    //默認打開”sheet1”(名稱任意),并將其轉(zhuǎn)換為字符串
    output = xlsread(filePath,sheet1);
    //返回該Excel文件中的數(shù)據(jù),返回值存入output數(shù)組中
    [row,col] = size(output);
    //獲取錄入數(shù)據(jù)的行列值,返回到VC平臺以供其拷貝指定內(nèi)存大小的數(shù)據(jù)
    output = reshape(output,row*col,1);
    //將該二維數(shù)組轉(zhuǎn)換為一維。這是第二部分二維數(shù)組處理的第一步
    保存文件后,使用deploytool工具將其制作成名為FileOpen的COM(封裝MCR),注冊該控件并將相應(yīng)的文件拷貝到MFC的工程中,對于基礎(chǔ)操作此處不再贅述。
    (2)在VC平臺下創(chuàng)建名為PCA的基于對話框的MFC工程,添加一打開文件的按鈕控件,ID為ID_FILE_OPEN。篇幅所限只給出部分核心代碼,首先在Dlg類中添加3個私有成員變量用以保存Matlab讀取的數(shù)據(jù)及行列值,即:
    double *m_originData; long   m_row, m_col;
    在響應(yīng)函數(shù)中添加如下代碼
    CFileDialog fileDlg(TRUE); 
    fileDlg.m_ofn.lpstrTitle="請選擇你的excel數(shù)據(jù)";
    fileDlg.m_ofn.lpstrFilter="Text Files(*.xls)\0*.xls\0All Files(*.*)\0*.*\0\0";
  if(IDOK==fileDlg.DoModal() {
     CString fileName; IFileOpenclass *pfile;
    VARIANT filePath,output,rowOutput,colOutput;
                                //m文件的輸入?yún)?shù)
    fileName = fileDlg.GetPathName();
                          //獲取文件路徑并存于fileName中
    CoInitialize(NULL);                                //COM初始化
    HRESULT hr = CoCreateInstance(CLSID_FileOpenclass,NULL,CLSCTX_ALL,IID_IFileOpenclass,(void**)&pfile);
                                    //新建COM實例
    VariantInit(&filePath);              //VARIANT數(shù)據(jù)初始化
    
    filePath.vt = VT_BSTR;           //指定filePath變量類型
    //將fileName中保存的指定文件路徑名保存至filePath參數(shù)中
    filePath.bstrVal = fileName.AllocSysString();
    pfile->CExcelRead(3,&output,&rowOutput, &colOutput,filePath);
    m_row=(long) rowOutput.dblVal;       
                                 //取出行列值及錄入數(shù)據(jù)
    m_col = (long) colOutput.dblVal;
     m_originData = (double *)malloc(sizeof(double)*m_row*m_
col); memcpy(m_originData,output.parray->pvData,m_row*m_
col*sizeof(double));
        //將matlab讀入數(shù)據(jù)保存到m_originData供后面程序使用
    } 上述C++代碼中省略了異常處理和相關(guān)的內(nèi)存、COM釋放代碼,由于代碼比較簡單所以不做進一步解析。請注意,下面將省略COM初始化及實例化等相同代碼。
    (3) 需要重點說明的是該方法的擴展性。通常Excel文件中保存多個工作區(qū),有時用戶可能需要打開同一Excel文件中不同工作區(qū)中的數(shù)據(jù),常規(guī)方法實現(xiàn)過于復(fù)雜,但對于本文介紹的方法可以通過修改添加幾條語句即可實現(xiàn)。首先,新建一個m文件用來處理工作區(qū)的選擇,代碼如下:
    function [sheet,col] = CSheetSelected(filePath)
    [typ, sheet] = xlsfinfo(filePath);
    [row,col] = size(sheet);
    由于返回值是一個cell數(shù)組,所以VC平臺要使用CStringArray數(shù)據(jù)結(jié)構(gòu)保存返回值,并顯示各工作區(qū)名稱供用戶選擇。然后,通過人機交互將用戶選擇的工作區(qū)參數(shù)保存并傳遞至CExcelRead.m,通過在CExcelRead.m增加一個工作區(qū)選擇參數(shù),并對代碼稍作修改即可。
2 二維數(shù)組參數(shù)的傳遞與輸出[2]
    下面以主成分分析為例介紹基于COM的Matlab與VC混合編程中二維數(shù)組參數(shù)處理。
    (1) 主成分分析pcamat.m代碼如下:
    function [eigenvector,eigenvalue] = pcamat(oriData,row,column)
    //在Excel讀入時已經(jīng)完成了二維數(shù)組輸出的關(guān)鍵步驟,即輸出時將二維數(shù)組轉(zhuǎn)換為一維數(shù)組。但在VC平臺接收還原為二維時要注意,Matlab數(shù)組存儲方式是按列存儲,而VC平臺下數(shù)組是按行存儲,所以轉(zhuǎn)換時0~row-1為第一列,row~2*row-1為第二列,以此類推。本文輸入?yún)?shù)oriData是一維數(shù)組,所以要將其還原為二維數(shù)組使Matlab程序能夠正常運行,即下一行代碼所示。
    oriData = reshape(oriData,row,column);
    dataSTD=std(oriData,0,1); dataMean = mean(oriData);
    dataSR = (oriData-dataMean(ones(row,1),:))./dataSTD(ones(row,1),:);
    [eigenvector,newdata,eigenvalue,Exa]=princomp(dataSR);
    //第三行至此處均為主成分分析內(nèi)容
    eigenvector = reshape(eigenvector,column*column,1);
    //與Excel文件讀取時類似,將二維輸出轉(zhuǎn)換為一維數(shù)組進行輸出
    (2) 保存后,封裝打包為COM組件,并完成注冊等相關(guān)操作。在PCA工程對話框上添加一個名為PCATest的按鈕控件,核心代碼如下:
    VARIANT oriData,row,column,eigVector,eigValue;
    VariantInit(&oriData);                             //參數(shù)初始化
    
    oriData.vt = VT_R8|VT_ARRAY;
                           //定義SAFEARRAY類型的一維數(shù)組
    SAFEARRAYBOUND rgsadound[1];
    rgsadound[0].lLbound = 0;
    rgsadound[0].cElements = m_row*m_col;
    oriData.parray = SafeArrayCreate(VT_R8,1,rgsadound);    
    oriData.parray->pvData = m_originData;
    //完成相關(guān)設(shè)置后,將第一步讀入的數(shù)據(jù)錄入到oriData中,即賦給pcamat的oriData。到此完成了二維數(shù)組的傳遞
    row.vt = VT_I4; col.vt = VT_I4;
    row.lVal = m_row; col.lVal = m_col;
    pca->pcamat(2,&eigVector,&eigValue,oriData,row,col);
    memcpy(result, eigVector.parray->pvData,m_col*m_col*
sizeof(double));
    綜上,二維數(shù)組參數(shù)處理就是使用reshape()函數(shù)對輸入輸出數(shù)據(jù)維數(shù)進行變換來完成操作。
3 Matlab圖像嵌入VC界面[3]
3.1 基于CWnd類的圖像嵌入

 


    在Windows操作系統(tǒng)下,所有應(yīng)用程序的窗口都是基于MFC中的CWnd類。所以可以通過調(diào)用該類或其派生類中的方法實現(xiàn)圖像嵌入?;舅枷耄涸贛atlab平臺下用COM封裝產(chǎn)生圖形窗口的程序;在VC平臺獲取Figure窗口的句柄,將Figure窗口設(shè)為VC程序的子窗口;(3)將Figure窗口移動到指定顯示位置。
    程序?qū)崿F(xiàn)如下:
    (1)將原pcamat.m進行修改,添加生成圖像的相關(guān)代碼
    function [eigenvector,eigenvalue] = pcamat(oriData,row,column,picName)
    //增加picName參數(shù),VC平臺下hFig將通過該名稱獲取生成圖像的句柄
    figure('NumberTitle','Off','MenuBar','None','ToolBar','Figure','Name',picName,'Units','Points');
    //圖像參數(shù)預(yù)設(shè),保留工具欄。使用Matlab提供的工具欄的所有功能是使用該方法的最大優(yōu)點
    percent = 100*eigenvalue /sum(eigenvalue);
                                         //計算貢獻率
    pareto(percent);                                              //畫圖
    xlabel('主成分');
    ylabel('方差占的比重(%)');
    (2)封裝打包成名為figure的COM組件,并完成注冊等相關(guān)操作。因為修改后的m文件運行結(jié)果包含輸出結(jié)果和圖像兩部分,所以下面有關(guān)圖像處理的代碼依然在PCATest控件的響應(yīng)函數(shù)中。
    CString WNDName = "Demo";         //自定義窗口名稱
    Ifigureclass *pic;
    VARIANT oriData, row, col, picName,eigVector, eigValue;    VariantInit(&picName);             
    ……    
    picName.vt = VT_BSTR;         //將自定義窗口名稱賦予
                                                  Matlab生成圖像
    picName.bstrVal = WNDName.AllocSysString();
    HWND hFig;  int timer = 50;
    //用死循環(huán)確??梢垣@取到圖像句柄,注意此處必須使用sleep(),給予系統(tǒng)足夠的響應(yīng)時間
    while(1){
        pic->pcamat(2,&eigVector,&eigValue,oriData,row,col,
            picName);   
            Sleep(timer);
        hFig = ::FindWindow(NULL,FigName);
          if(hFig != NULL){
            break;
        }
        timer += 10;
        pic->Release();
        }               
    long lStyle = ::GetWindowLong(hFig,GWL_STYLE);  //設(shè)置Figure窗口樣式。
    //注意SetWindowLong()和SetWindowPos()先后順序,詳見MSDN
    ::SetWindowLong(hFig,GWL_STYLE,lStyle&(~WS_CAPTION)&(~WS_THICKFRAME))
    ::SetWindowPos(hFig,NULL,0,0,0,0,SWP_NOMOVE|SWP_
        NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_
        FRAMECHANGED);CRect PlotRec;
    CWnd *PlotArea = GetDlgItem(IDC_STATIC_FIGURE);                                       //設(shè)置圖像顯示區(qū)域
    PlotArea->GetWindowRect(&PlotRec);
    long Width = PlotRec.right - PlotRec.left;
    long Height = PlotRec.bottom - PlotRec.top;
    ::SetParent(hFig,PlotArea->GetSafeHwnd());
                                    //設(shè)置圖像的父窗口
    ::SetWindowPos(hFig,NULL,0,0,Width,Height,SWP_NOZORDER|SWP_NOACTIVATE);
    運行結(jié)果如圖1所示。該方法的缺點是,在圖像生成時會有閃爍現(xiàn)象。而優(yōu)點是前面提到的可以繼續(xù)使用Matlab提供的工具欄。鑒于該缺點影響整體美觀,所以引入下面第二種方法。

3.2基于Bitmap類的圖像嵌入
    通過Bitmap類將Matlab生成的.bmp文件加載到VC工程中,使用Bitmap類中的成員函數(shù)對圖像進行處理。由于相關(guān)函數(shù)可以通過幫助手冊獲取,所以此處不再給出相應(yīng)代碼,運行結(jié)果如圖2所示。該方法避免了方法一生成圖像時的閃爍現(xiàn)象,但是BMP圖像顯示效果較差而且無法使用Matlab提供的工具菜單,這是其不足之處。

    使用COM組件進行混合編程時,往往習(xí)慣于在VC平臺下思考所遇到的問題,但是這樣不僅使得問題可能變得復(fù)雜化,或得不到妥善解決,而且也違背了“混合”的初衷。二維數(shù)組參數(shù)處理就是一個很好的例證。其次,充分利用Matlab特性可以使得程序具備良好的擴展性和穩(wěn)定性,對Excel文件讀取方式進行的擴展,明顯使軟件更加人性化。對于圖像嵌入問題,雖然文中提出的兩種嵌入方式可以滿足基本需求,但是仍然存在一些瑕疵,還需要進一步研究。
參考文獻
[1] MCKINNEY B. Article 5. The Safe OLE Way of Handling  Arrays.MSDN, 2001.
[2] 劉維.精通Matlab與C++混合程序設(shè)計(第2版)[M].北京:北京航空航天大學(xué)出版社,2008.
[3] 董維國.深入淺出Matlab 7.X混合編程[M].北京:機械工業(yè)出版社,2006.

此內(nèi)容為AET網(wǎng)站原創(chuàng),未經(jīng)授權(quán)禁止轉(zhuǎn)載。