新聞中心

        EEPW首頁 > EDA/PCB > 設計應用 > Matlab強大的數組處理功能如何解決圖像處理解析方案

        Matlab強大的數組處理功能如何解決圖像處理解析方案

        作者: 時間:2016-10-18 來源:網絡 收藏

        基于COM組件的與C++混合編程方式因擁有獨立的運行環境和兩種語言的互補優勢而被眾多科研人員和編程人員所接受,同時也是MathWorks公司推薦使用的混編方式。但在程序設計過程中,通常會遇到兩個難點問題:(1)二維參數的傳遞與輸出; (2)生成圖形嵌入VC工程界面中。對于第一個問題,Bruce McKinney[1]在MSDN上指出;“如果對一維進行操作,則SAFEARRAY函數變的簡單且易操作。但是對于多維,同樣的操作要復雜得多”,造成這一問題的根源在于與C++對多維數組元素的存儲方式不同。而在本項目開發過程中回避了這個復雜的過程,轉而利用Matlab強大的數組將其解決。Matlab完善的圖形是其被科研人員所推崇的原因之一,但因其圖形擁有獨立的窗口,嚴重影響了整體界面美觀和用戶交互體驗,所以Matlab圖形嵌入是混合編程中不可回避的問題。對此將通過實例介紹兩種圖形嵌入方法并分析說明其優缺點和相關細節。

        本文引用地址:http://www.104case.com/article/201610/308611.htm

        本文示例均在Matlab R2008a和VC 6.0平臺下完成,且通過調試能夠正常運行。

        1 Excel文件讀取方法

        VC平臺下的Excel文件讀取方式復雜難懂,但如果使用Matlab與VC的混合編程方式編寫將更加簡便,而且擁有較強的可擴展性。下面代碼詳細講解了該過程。

        (1)在Matlab平臺下建立CExcelRead.m文件,代碼如下:

        function [output,row,col] = CExcelRead(filePath)

        //filePath包含了指定文件的路徑和文件名,由VC平臺函數GetPathName()獲取

        [typ, desc] = xlsfinfo(filePath);

        //xlsfinfo()可獲取指定Excel文件中各個sheet工作區的名稱,返回值存入名為desc的cell數組中

        sheet1 = cell2mat(desc(1));

        //默認打開”sheet1”(名稱任意),并將其轉換為字符串

        output = xlsread(filePath,sheet1);

        //返回該Excel文件中的數據,返回值存入output數組中

        [row,col] = size(output);

        //獲取錄入數據的行列值,返回到VC平臺以供其拷貝指定內存大小的數據

        output = reshape(output,row*col,1);

        //將該二維數組轉換為一維。這是第二部分二維數組處理的第一步

        保存文件后,使用deploytool工具將其制作成名為FileOpen的COM(封裝MCR),注冊該控件并將相應的文件拷貝到MFC的工程中,對于基礎操作此處不再贅述。

        (2)在VC平臺下創建名為PCA的基于對話框的MFC工程,添加一打開文件的按鈕控件,ID為ID_FILE_OPEN。篇幅所限只給出部分核心代碼,首先在Dlg類中添加3個私有成員變量用以保存Matlab讀取的數據及行列值,即:

        double *m_originData; long m_row, m_col;

        在響應函數中添加如下代碼

        CFileDialog fileDlg(TRUE);

        fileDlg.m_ofn.lpstrTitle=請選擇你的excel數據;

        fileDlg.m_ofn.lpstrFilter=Text Files(*.xls)*.xlsAll Files(*.*)*.*;

        if(IDOK==fileDlg.DoModal() {

        CString fileName; IFileOpenclass *pfile;

        VARIANT filePath,output,rowOutput,colOutput;

        //m文件的輸入參數

        fileName = fileDlg.GetPathName();

        //獲取文件路徑并存于fileName中

        CoInitialize(NULL); //COM初始化

        HRESULT hr = CoCreateInstance(CLSID_FileOpenclass,NULL,CLSCTX_ALL,IID_IFileOpenclass,(void**)pfile);

        //新建COM實例

        VariantInit(filePath); //VARIANT數據初始化

        filePath.vt = VT_BSTR; //指定filePath變量類型

        //將fileName中保存的指定文件路徑名保存至filePath參數中

        filePath.bstrVal = fileName.AllocSysString();

        pfile->CExcelRead(3,output,rowOutput, colOutput,filePath);

        m_row=(long) rowOutput.dblVal;

        //取出行列值及錄入數據

        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讀入數據保存到m_originData供后面程序使用

        }

        上述C++代碼中省略了異常處理和相關的內存、COM釋放代碼,由于代碼比較簡單所以不做進一步解析。請注意,下面將省略COM初始化及實例化等相同代碼。

        (3) 需要重點說明的是該方法的擴展性。通常Excel文件中保存多個工作區,有時用戶可能需要打開同一Excel文件中不同工作區中的數據,常規方法實現過于復雜,但對于本文介紹的方法可以通過修改添加幾條語句即可實現。首先,新建一個m文件用來處理工作區的選擇,代碼如下:

        function [sheet,col] = CSheetSelected(filePath)

        [typ, sheet] = xlsfinfo(filePath);

        [row,col] = size(sheet);

        由于返回值是一個cell數組,所以VC平臺要使用CStringArray數據結構保存返回值,并顯示各工作區名稱供用戶選擇。然后,通過人機交互將用戶選擇的工作區參數保存并傳遞至CExcelRead.m,通過在CExcelRead.m增加一個工作區選擇參數,并對代碼稍作修改即可。

        2 二維數組參數的傳遞與輸出[2]

        下面以主成分分析為例介紹基于COM的Matlab與VC混合編程中二維數組參數處理。

        (1) 主成分分析pcamat.m代碼如下:

        function [eigenvector,eigenvalue] = pcamat(oriData,row,column)

        //在Excel讀入時已經完成了二維數組輸出的關鍵步驟,即輸出時將二維數組轉換為一維數組。但在VC平臺接收還原為二維時要注意,Matlab數組存儲方式是按列存儲,而VC平臺下數組是按行存儲,所以轉換時0~row-1為第一列,row~2*row-1為第二列,以此類推。本文輸入參數oriData是一維數組,所以要將其還原為二維數組使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);

        //第三行至此處均為主成分分析內容

        eigenvector = reshape(eigenvector,column*column,1);

        //與Excel文件讀取時類似,將二維輸出轉換為一維數組進行輸出

        (2) 保存后,封裝打包為COM組件,并完成注冊等相關操作。在PCA工程對話框上添加一個名為PCATest的按鈕控件,核心代碼如下:

        VARIANT oriData,row,column,eigVector,eigValue;

        VariantInit(oriData); //參數初始化

        oriData.vt = VT_R8|VT_ARRAY;

        //定義SAFEARRAY類型的一維數組

        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;

        //完成相關設置后,將第一步讀入的數據錄入到oriData中,即賦給pcamat的oriData。到此完成了二維數組的傳遞

        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));

        綜上,二維數組參數處理就是使用reshape()函數對輸入輸出數據維數進行變換來完成操作。

        3 Matlab圖像嵌入VC界面[3]

        3.1 基于CWnd類的圖像嵌入

        在Windows操作系統下,所有應用程序的窗口都是基于MFC中的CWnd類。所以可以通過調用該類或其派生類中的方法實現圖像嵌入。基本思想:在Matlab平臺下用COM封裝產生圖形窗口的程序;在VC平臺獲取Figure窗口的句柄,將Figure窗口設為VC程序的子窗口;(3)將Figure窗口移動到指定顯示位置。

        程序實現如下:

        (1)將原pcamat.m進行修改,添加生成圖像的相關代碼

        function [eigenvector,eigenvalue] = pcamat(oriData,row,column,picName)

        //增加picName參數,VC平臺下hFig將通過該名稱獲取生成圖像的句柄

        figure(NumberTitle,Off,MenuBar,None,ToolBar,Figure,Name,picName,Units,Points);

        //圖像參數預設,保留工具欄。使用Matlab提供的工具欄的所有功能是使用該方法的最大優點

        percent = 100*eigenvalue /sum(eigenvalue);

        //計算貢獻率

        pareto(percent); //畫圖

        xlabel(主成分);

        ylabel(方差占的比重(%));

        (2)封裝打包成名為figure的COM組件,并完成注冊等相關操作。因為修改后的m文件運行結果包含輸出結果和圖像兩部分,所以下面有關圖像處理的代碼依然在PCATest控件的響應函數中。

        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;

        //用死循環確保可以獲取到圖像句柄,注意此處必須使用sleep(),給予系統足夠的響應時間

        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); //設置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); //設置圖像顯示區域

        PlotArea->GetWindowRect(PlotRec);

        long Width = PlotRec.right - PlotRec.left;

        long Height = PlotRec.bottom - PlotRec.top;

        ::SetParent(hFig,PlotArea->GetSafeHwnd());

        //設置圖像的父窗口

        ::SetWindowPos(hFig,NULL,0,0,Width,Height,SWP_NOZORDER|SWP_NOACTIVATE);

        該方法的缺點是,在圖像生成時會有閃爍現象。而優點是前面提到的可以繼續使用Matlab提供的工具欄。鑒于該缺點影響整體美觀,所以引入下面第二種方法。

        3.2基于Bitmap類的圖像嵌入

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

        使用COM組件進行混合編程時,往往習慣于在VC平臺下思考所遇到的問題,但是這樣不僅使得問題可能變得復雜化,或得不到妥善解決,而且也違背了“混合”的初衷。二維數組參數處理就是一個很好的例證。其次,充分利用Matlab特性可以使得程序具備良好的擴展性和穩定性,對Excel文件讀取方式進行的擴展,明顯使軟件更加人性化。對于圖像嵌入問題,雖然文中提出的兩種嵌入方式可以滿足基本需求,但是仍然存在一些瑕疵,還需要進一步研究。



        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 靖远县| 呼伦贝尔市| 汉寿县| 新龙县| 太白县| 白城市| 腾冲县| 松溪县| 濮阳县| 淅川县| 洪湖市| 山阳县| 永平县| 洛隆县| 和平县| 井冈山市| 邛崃市| 长葛市| 虎林市| 故城县| 庆阳市| 夏邑县| 稷山县| 岚皋县| 枝江市| 屏边| 高要市| 裕民县| 慈溪市| 茂名市| 疏勒县| 资溪县| 江安县| 民丰县| 通榆县| 辽阳县| 盐城市| 栖霞市| 泗水县| 鄱阳县| 九台市|