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