利用MFC的Picture控件顯示和處理圖像

在《OpenCV教程-基礎篇》的2.8節中,所創建的MFC圖像顯示是直接放在對話框面板的左上角的,感覺不大美觀。在MFC快速應用opencv一書中則是介紹用SDI(單文檔界面)來顯示圖像,A step-by-step guide to the use of Microsoft Visual C++ and theIntel OpenCV library使用VS2005來進行圖像和視頻的讀取和處理,但是其圖像和視頻的顯示界面不是在對話框裏面的,而是新建一個窗口來做。所以下面我們就來看看怎麼在對話框裏使用Picture控件來顯示和處理圖像。

創建MFC

首先創建一個MFC對話框應用程序(Dialog-based Application)如下:

Image:mfc_picctl_1.JPG

Image:mfc_picctl_2.JPG


在VS2005和2008裏,我們可以用一個 Solution 來組合幾個 Project (每個 Project 基本上只包含一個 Program),當我們要構建一個多Program的應用時(例如一個客戶端程序加一個服務器應用程序),利用 Solution 可以將這些 Projects 組合起來、並且共享文件和函數庫。通常需要爲Solution創建一個主路徑,其中包含了所有Projects的路徑。不過在這篇文章裏,我們只構建一個簡單的Project,所以在創建MFC的New Project對話框裏,不用勾選“Create directory for solution”這個選項。

點擊OK -- Next進入下一步,在這裏我們創建一個Dialog-based Application,大部分選項按默認設置就行,不過最下面的“Use Unicode libraries”最好去掉。如果勾選了這個選項,程序代碼就會使用16bit的Unicode字符集來編碼,但是很多函數雖然使用 char* (ASCII stings) 類型字符,而將字符串從 Unicode 轉換到 ASCII 是非常麻煩的。使用 Unicode 在編譯時可能會遇到下列錯誤:

cannot convert parameter 1 from 'CString' to 'const char *'
cannot convert from 'const char [11]' to 'LPCWSTR'

這意味着在Unicode和Multi-byte字符串的轉換中出現了問題。在上一篇學習筆記中,就提到“成員函數LoadBMP其輸入參數類型應爲 const char*”,那應該只是一個治標的方法,這裏的去掉“Use Unicode libraries”選項,纔是治本之道。 往後的幾步設置,可以根據自己的需要來操作,我的設置如下:

Image:mfc_picctl_3.JPG

Image:mfc_picctl_4.JPG


編寫代碼

在Resource View面板->mymfc(工程名稱)->mymfc.rc->Dialog雙擊IDD_MYMFC_DIALOG,可以看到一個初始的GUI界面,往裏面添加兩個 Button 和 一個 Picture 控件,如下:

Image:mfc_picctl_5.JPG

選中單個控件、右擊選擇屬性(Properties),可以看到控件的ID號,這個號可以自行編輯,例如 Picture 控件的 ID 號我設置爲 IDC_ShowImg,這個 ID 號在後面的圖像顯示函數中要用到。 首先在項目屬性中加載lib文件:菜單Project -> Properties -> Configuration Properties -> Linker –> Input -> additional dependencies 中加入 cxcore200.lib cv200.lib highgui200.lib。 然後在 mymfc.h 的 #include "resource.h" 下加入如下代碼:

#include "cv.h"#include "highgui.h"#define IMAGE_WIDTH 256#define IMAGE_HEIGHT 256#define IMAGE_CHANNELS 3

在 Class View 面板右擊 CmymfcDlg,選擇 Add –> Add Variable,添加一個 IplImage* 類型的變量 TheImage;再點擊 CmymfcDlg,在下面窗口的列表中雙擊 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 );

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

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

接着在 CmymfcApp 下面的成員列表中雙擊 InitInstance,在兩個“// TODO: Place code here to handle when the dialog is…”下面添加:

cvReleaseImage(&dlg.TheImage);

即按下“OK”或“Cancel”時,釋放TheImage佔用的內存。 接下來就是寫讀取和處理圖片的功能函數了。 回到 mymfc 的 GUI 編輯界面中,右擊按鈕 ReadImg,選擇 Add Event Handler,建立按鈕點擊的消息響應程序:

Image:mfc_picctl_6.JPG

句柄名設置爲 OnBnClickedReadimg,主要的響應操作包括 彈出對話框選擇圖片文件、讀入圖片文件、對圖片統一縮放至256*256的大小、顯示圖像,代碼如下:

// TODO: Add your control notification handler code here// 選項圖片的約定
	CFileDialog dlg(TRUE, _T("*.bmp"),NULL,
		OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
		_T("p_w_picpath files (*.bmp; *.jpg) |*.bmp; *.jpg | All Files (*.*) |*.*||"),NULL);// 打開文件對話框的標題名
	dlg.m_ofn.lpstrTitle= _T("Open Image");// 判斷是否獲得圖片if( dlg.DoModal()!= IDOK )return;// 獲取圖片路徑
	CString mPath = dlg.GetPathName();// 讀取圖片、緩存到一個局部變量 ipl 中
	IplImage* ipl = cvLoadImage( mPath,1);// 判斷是否成功讀取圖片if(!ipl )return;// 對上一幅顯示的圖片數據清零if( TheImage )cvZero( TheImage );// 對讀入的圖片進行縮放,使其寬或高最大值者剛好等於 256,再複製到 TheImage 中
	ResizeImage( ipl );// 調用顯示圖片函數
	ShowImage( TheImage, IDC_ShowImg );// 釋放 ipl 佔用的內存
	cvReleaseImage(&ipl );

其中包含了兩個新的成員函數 ResizeImage 和 ShowImage,前者的作用是對讀入的不同大小的圖像進行縮放,再通過設置 ROI 的方式將圖像存入 256*256 的 TheImage 中;後者是將圖像 TheImage 顯示到圖片顯示控件 IDC_ShouImg 窗口的正中部位。爲了實現這兩個功能,首先在 Class View 面板右擊 CmymfcDlg,選擇 Add –> Add Function,創建兩個函數:void ShowImage( IplImage* img, UINT ID ) 和 void ResizeImage(IplImage* img)。以下是這兩個函數的實現代碼:

void CmymfcDlg::ResizeImage(IplImage* img){// 讀取圖片的寬和高int w = img->width;int h = img->height;// 找出寬和高中的較大值者int max =(w > h)? w: h;// 計算將圖片縮放到TheImage區域所需的比例因子float scale =(float)((float) max /256.0f);// 縮放後圖片的寬和高int nw =(int)( w/scale );int nh =(int)( h/scale );// 爲了將縮放後的圖片存入 TheImage 的正中部位,需計算圖片在 TheImage 左上角的期望座標值int tlx =(nw > nh)?0:(int)(256-nw)/2;int tly =(nw > nh)?(int)(256-nh)/2:0;// 設置 TheImage 的 ROI 區域,用來存入圖片 img
	cvSetImageROI( TheImage, cvRect( tlx, tly, nw, nh));// 對圖片 img 進行縮放,並存入到 TheImage 中
	cvResize( img, TheImage );// 重置 TheImage 的 ROI 準備讀入下一幅圖片
	cvResetImageROI( TheImage );}void CmymfcDlg::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 );// 求出圖片控件的寬和高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 );}

函數 ResizeImage 是參考了學習筆記(5)中單窗口顯示多幅圖像的函數 cvShowMultiImages 修改而成的,函數 ShowImage 則是參考了帖子《OpenCV如何把圖像顯示到MFCpicture控件上》的代碼,另外下面幾個帖子也可以參考:

1、《MFC picture control畫框的問題》

2、《MFC picture control控件實現(隱藏)文字顯示》

3、《MFCPictureControl中顯示圖片(jpg)遇到的問題》

4、《vc怎樣在picturecontrol中顯示jpg,jif,bmp格式圖象》

5、《使用Picture Control顯示BMP圖片》

最後是要對讀入的圖像做簡單的Canny邊緣處理,爲此,建立一個按鈕 EdgeDetect,相應的響應代碼如下:

void CmymfcDlg::OnBnClickedEdgedetect(){// TODO: Add your control notification handler code hereIplImage *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 );    
    ShowImage( TheImage, IDC_ShowImg );// 調用顯示圖片函數
 
    cvReleaseImage(&gray );cvReleaseImage(&edge );}

這裏主要是參考了《OpenCV教程-基礎篇》P33的代碼,不過並沒有像書中那樣創建新的 MyIplClass 類來進行圖像的讀取和處理操作。我覺得這篇文章中直接對 TheImage 進行處理可能不大妥當,按照教程那樣新建一個類來處理纔是比較穩妥吧。這裏涉及到函數返回對象實例的過程、深拷貝和淺拷貝、拷貝構造、動態內存的使用、opencv相關接口的代碼等方面,以後還要進一步深入學習和理解!

我們還可以在Resource View面板->mymfc(工程名稱)->mymfc.rc->Version中修改程序的產品版本、名稱等信息。

來看看程序生成後的效果:

Image:mfc_picctl_7.JPG

Image:mfc_picctl_8.JPG

Image:mfc_picctl_9.JPG


可以到這裏下載源碼: http://download.csdn.net/source/1779188

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