在《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控件來顯示和處理圖像。php
首先建立一個MFC對話框應用程序(Dialog-based Application)以下:
windows
在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」選項,纔是治本之道。 日後的幾步設置,能夠根據本身的須要來操做,個人設置以下:
ide
在Resource View面板->mymfc(工程名稱)->mymfc.rc->Dialog雙擊IDD_MYMFC_DIALOG,能夠看到一個初始的GUI界面,往裏面添加兩個 Button 和 一個 Picture 控件,以下:
函數
選中單個控件、右擊選擇屬性(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 的初始化代碼:ui
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…」下面添加:spa
cvReleaseImage(&dlg.TheImage);
即按下「OK」或「Cancel」時,釋放TheImage佔用的內存。 接下來就是寫讀取和處理圖片的功能函數了。 回到 mymfc 的 GUI 編輯界面中,右擊按鈕 ReadImg,選擇 Add Event Handler,創建按鈕點擊的消息響應程序:
句柄名設置爲 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如何把圖像顯示到MFC的picture控件上》的代碼,另外下面幾個帖子也能夠參考:
1、《MFC picture control畫框的問題》
2、《MFC picture control控件實現(隱藏)文字顯示》
3、《MFC在PictureControl中顯示圖片(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中修改程序的產品版本、名稱等信息。
來看看程序生成後的效果:
能夠到這裏下載源碼: http://download.csdn.net/source/1779188