OpenCV GUI之搭建MFC+OpenCV開發環境

步驟:

1  創建SDI MFC工程,單文檔,最好選擇use MFC As a static library  (以防止MFC中使用opencv的內存泄露問題)

2  加入OpenCV庫支持[頭文件和庫文件]:菜單Project->Settings->Link->Input->Object/library modules中加入cxcore.lib cv.lib ml.lib cvaux.lib highgui.lib cvcam.lib

3  在Doc類開頭添加HighGui.h 的頭文件包含, 同時添加一個變量CImage m_image; [在定義處,建議public,VC下]

4  添加2個虛函數,用來打開和保存文件:OnOpenDocument 和 OnSaveDocument  如下:

BOOL CSDI OpenCVDoc::OnOpenDocument(LPCTSTR lpszPathName){
    if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE;
    // TODO: Add your specialized creation code here
    m_image.Load(lpszPathName);
    return TRUE;
}

BOOL CSDI OpenCVDoc::OnSaveDocument(LPCTSTR lpszPathName){
    // TODO: Add your specialized code here and/or call the base class
    m_image.Save(lpszPathName);
    return TRUE;

   // return CDocument::OnSaveDocument(lpszPathName); //原因:會導致保存後的圖片大小變爲0  
}

5   在View類添加顯示函數

void CSDI_OpenCVView::OnDraw(CDC* pDC)
{
        CSDI_OpenCVDoc* pDoc = GetDocument();
        ASSERT_VALID(pDoc);
        // TODO: add draw code for native data here
        CImage & img = pDoc ->m_image; //CImage類 其實就是CvvImage類,可以直接替換使用

        CRect r;
        GetClientRect (&r);
        img.DrawToHDC(pDC->GetSafeHdc() ,r);
}

6  添加菜單,實現CANNY

①修改前面的 m_image.Load(lpszPathName);爲 m_image.Load(lpszPathName,0); //強制轉換讀取圖像爲8位深度灰度圖

②添加菜單ImageProcessing,子菜單CANNY,在Doc類中添加ID號爲IDM_CANNY的COMMAND消息響應函數,代碼如下

void CSDI_OpenCVDoc::OnCanny() //菜單處理函數

{

// TODO: Add your command handler code here

IplImage* img;

img=m_image.GetImage();//由CImage類 返回一個IplImage* 指針,因爲:IplImage* GetImage() { return m_img; };

cvCanny(img,img,50,150,3);

UpdateAllViews(NULL);

}

說明

  1. MFC中快速應用OpenCV教程
  2. 關於圖片太大,無法放入view裏的一個解決方案,請看 feixiaolin 的帖子 opencv 數據讀寫操作+圖像噪聲+ MFC下OpenCV源代碼 以及 如何插入滾動條
  3. OpenCV繪圖函數DrawToHDC的一個簡要解釋
  4. 在opencv1.0版本中,使用上面介紹的canny的方法,存在單通道圖片無法打開的錯誤,具體的原因請看這裏,這主要是由於opencv1.0中的一個bug所導致的。原因
  5. 在opencv 1.1版本中使用,可能導致MFC的程序無法退出,解決辦法請看這裏 程序無法正常退出    解決:1.1下不能退出的問題 需要將1.1中_highgui.h中的 #define HAVE_VIDEOINPUT 1這句 註釋掉,重新編譯OpenCv的Highgui工程,重新生成Highgui.lib就可以了 
  6. 注意,其中的 CImage 其實就是 CvvImage 這兩個可以直接替換使用。
  7. OLE錯誤對話框的問題,請看這裏 如何解決OLE的問題的論壇討論

參考資料

http://www.opencv.org.cn/index.php/MFC%E4%B8%AD%E5%BF%AB%E9%80%9F%E5%BA%94%E7%94%A8OpenCV   MFC中快速應用OpenCV

http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=4932&p=18215#p18136   相關問題

對話框程序   VC6.0+OpenCV1.0

步驟:

1  創建工程:MFC_OpenCV_Dlg     對話框

2  加入OpenCV庫支持[頭文件和庫文件]

菜單Project->Settings->Link->Input->->Object/library modules 加入 cxcore.lib cv.lib ml.lib cvaux.lib highgui.lib cvcam.lib

3  添加頭文件和全局宏

在CMFC_OpenCV_Dlg.h開頭 #include "resource.h"  下 加入如下代碼

#include "cv.h"

#include "HighGUI.h"

#define IMAGE_WIDTH 512  //此處假設讀入和顯示的圖像都是512*512(像素)大小的彩色圖像,讀者可自行修改

#define IMAGE_HEIGHT 512

#define IMAGE_CHANNELS 3

4  添加IplImage*變量並初始化及內存釋放

在CMFC_OpenCV_DlgDlg  右擊添加一個 IplImage* 類型的變量 TheImage,

再雙擊該類下的OnInitDialog,在“// TODO: Add extra initialization here”下面添加TheImage 的初始化代碼:

    CvSize ImgSize;
    ImgSize.height = IMAGE_HEIGHT;
    ImgSize.width = IMAGE_WIDTH;
    TheImage = cvCreateImage( ImgSize, IPL_DEPTH_8U, IMAGE_CHANNELS );

接着在CMFC_OpenCV_DlgApp 下面的成員列表中雙擊 InitInstance,

在兩個“// TODO: Place code here to handle when the dialog is…”下面添加:

cvReleaseImage( &dlg.TheImage );//釋放內存

即按下“OK”或“Cancel”時,釋放TheImage佔用的內存。//按下OK或Cancel 會調用InitInstance,看源代碼

5  窗口重繪

在CMFC_OpenCV_DlgDlg ,雙擊 OnPaint,在 if(IsIconic())…的 else 裏添加以下代碼,用來重繪窗口:

        CDialog::OnPaint();                    // 重繪對話框
        CDialog::UpdateWindow();          // 更新windows窗口,如果無這步調用,圖片顯示還會出現問題
        ShowImage( TheImage, IDC_ImgShowCtrl );    // 重繪圖片函數

6  添加控件及成員函數

1個Edit

 

關聯變量m_Path

 

1個picture

ID:IDC_ImgShowCtrl

 

 

4個Button

ID:IDC_ChooseFile   用來選擇圖片並保存路徑到m_Path

ID:IDC_ShowImage     用來顯示m_Path路徑上的圖片

ID:IDC_EdgeDetect  邊緣檢測測試

ID:IDC_Test               擴展/自定義

消息響應函數:OnChooseFile()

消息響應函數:OnShowImage()

消息響應函數:OnEdgeDetect()

消息響應函數:OnTest()

 詳細代碼見下

在CMFC_OpenCV_DlgDlg,添加兩個成員函數void ShowImage( IplImage* img, UINT ID ) 和 void ResizeImage(IplImage* img),代碼如下:

void CMFC_OpenCV_DlgDlg::ShowImage(IplImage *img, UINT ID) //ID 是Picture Control控件的ID號

{

// 獲得顯示控件的 DC

CDC* pDC = GetDlgItem( ID ) ->GetDC();

// 獲取 HDC(設備句柄) 來進行繪圖操作                

HDC hDC = pDC ->GetSafeHdc();                                

CRect rect;

GetDlgItem(ID) ->GetClientRect( &rect );

/*

CBrush brush(RGB(255,255,255));

pDC->FillRect(&rect,&brush);

*/

// 求出圖片控件的寬和高

int rw = rect.right - rect.left;                        

int rh = rect.bottom - rect.top;

// 讀取圖片的寬和高

int iw = img->width;        

int ih = img->height;

// 使圖片的顯示位置正好在控件的正中

int tx = (int)(rw - iw)/2;        

int ty = (int)(rh - ih)/2;

SetRect( rect, tx, ty, tx+iw, ty+ih );//

// 複製圖片

CvvImage cimg;

cimg.CopyOf( img );

// 將圖片繪製到顯示控件的指定區域內        

cimg.DrawToHDC( hDC, &rect );        

ReleaseDC( pDC );

}

void CMFC_OpenCV_DlgDlg::ResizeImage(IplImage *img)

{

// 圖片的大小

int width=(int)IMAGE_WIDTH;                        // 寬

int height=(int)IMAGE_HEIGHT;                // 高

float max_w_h=(float) (IMAGE_WIDTH>IMAGE_HEIGHT)? IMAGE_WIDTH : IMAGE_HEIGHT;

// 讀取圖片的寬和高

    int w = img->width;

    int h = img->height;// 找出寬和高中的較大值者

    int max = (w > h)? w: h;

// 計算將圖片縮放到TheImage區域所需的比例因子

    //float scale = (float) ( (float) max / 512.0f );

float scale = (float) ( (float) max / max_w_h );

   

    // 縮放後圖片的寬和高

    int nw = (int)( w/scale );

    int nh = (int)( h/scale );

// 爲了將縮放後的圖片存入 TheImage 的正中部位,需計算圖片在 TheImage 左上角的期望座標值

    int tlx = (nw > nh)? 0: (int)(width-nw)/2;

    int tly = (nw > nh)? (int)(height-nh)/2: 0;

/*        //確保爲3通道圖像

IplImage *temp = 0; //定義中轉圖像指針

temp = cvCreateImage( cvGetSize(img), img->depth, 3);

if(1==img->nChannels)        // 如果是單通道的 圖像,轉換成3通道的

{

cvConvertImage(img,temp,CV_CVTIMG_SWAP_RB);        // P70

//CV_CVTIMG_FLIP   (flip vertically)    CV_CVTIMG_SWAP_RB (swap the R and B channels)

}

else if(3==img->nChannels)

{

temp=cvCloneImage(img);        

}

else AfxMessageBox(_T("channel wrong on function show picture !"));

*/

// 設置 TheImage 的 ROI 區域,用來存入圖片 img

    cvSetImageROI( TheImage, cvRect( tlx, tly, nw, nh) ); //P87

// 對圖片 img 進行縮放,並存入到 TheImage 中

    cvResize( img, TheImage,CV_INTER_AREA ); //P255

// 重置 TheImage 的 ROI 準備讀入下一幅圖片

    cvResetImageROI( TheImage );

//cvReleaseImage(&temp);        // 釋放 中轉 圖像指針

}

void CMFC_OpenCV_DlgDlg::OnChooseFile()

{

// TODO: Add your control notification handler code here

CFileDialog dlg(

        TRUE, NULL, NULL,

        OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,

        _T("All Files (*.*) |*.*||"), AfxGetMainWnd()

        );                                        // 選項圖片的約定

    dlg.m_ofn.lpstrTitle = _T("Open Image");    // 打開文件對話框的標題名

    if( dlg.DoModal() != IDOK )                    // 判斷是否獲得圖片

        return;

   

    m_Path.SetWindowText( dlg.GetPathName() );            // 獲取圖片路徑

}

void CMFC_OpenCV_DlgDlg::OnShowImage()

{

// TODO: Add your control notification handler code here

CString mPath;

m_Path.GetWindowText(mPath);

if(mPath=="")

{

AfxMessageBox(_T("file path is empty ,please choose file!"));

return;

}

IplImage* ipl = cvLoadImage( mPath, CV_LOAD_IMAGE_COLOR );    // 讀取圖片、緩存到一個局部變量 ipl 中

    if( !ipl )                                    // 判斷是否成功載入圖片

        return;

    if( TheImage )                                // 對上一幅顯示的圖片數據清零

        cvZero( TheImage );

   

ResizeImage( ipl );    // 對讀入的圖片進行縮放,使其寬或高最大值者剛好等於 512,再複製到 TheImage 中

    ShowImage( TheImage, IDC_ImgShowCtrl );            // 調用顯示圖片函數   

    cvReleaseImage( &ipl );                        // 釋放 ipl 佔用的內存

}

void CMFC_OpenCV_DlgDlg::OnEdgeDetect()

{

// TODO: Add your control notification handler code here

CString mPath;

m_Path.GetWindowText(mPath);

IplImage *gray = 0, *edge = 0;

    gray = cvCreateImage( cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 1 );

    edge = cvCreateImage( cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 1 );

    cvCvtColor( TheImage, gray, CV_BGR2GRAY );

    cvCanny( gray, edge, 30, 100, 3 );

    cvCvtColor( edge, TheImage, CV_GRAY2BGR );  

if(!TheImage)

{

AfxMessageBox(_T("PictureCtrl is empty ,please choose Pictrue!"));

return;

}

    ShowImage( TheImage, IDC_ImgShowCtrl );            // 調用顯示圖片函數

    cvReleaseImage( &gray );

    cvReleaseImage( &edge );

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章