Opencv (Opencv2)結合MFC學習數字圖像處理---顯示圖片


本文介紹在MFC框架下,使用opencv的解碼函數對圖片進行解碼,並最終顯示到窗口。在此過程中,遇到了圖片顯示時的大小問題,以及閃爍問題,本文將一一解決。 

【顯示圖片】

1. 在CImageProcessView::OnDraw(CDC* pDC) 中寫繪製圖片的代碼
    我們已經打開圖片時,利用opencv對圖片文件進行了解碼,圖像數據已經在src_image中持有,現在需要把src_image中的數據繪製到窗口。

複製代碼
 1 void CImageProcessView::OnDraw(CDC* pDC)
 2 {
 3     CImageProcessDoc* pDoc = GetDocument();
 4     ASSERT_VALID(pDoc);
 5     if (!pDoc)
 6         return;
 7 
 8     // TODO: add draw code for native data here
 9     Mat & image = pDoc->src_image;
10 }
複製代碼

 2. 將Mat轉化成CImage

 

       Mat是表示圖像數據的一個矩陣,它不能直接繪製到窗口DC,通過google,我發現atl的一個類CImage有繪製到DC的方法,所以只需要把Mat在顯示之前先轉化成CImage,代碼如下:

複製代碼
 1 void ImageUtility::MatToCImage( Mat &mat, CImage &cImage)  
 2 {  
 3     int width    = mat.cols;  
 4     int height   = mat.rows;  
 5     int channels = mat.channels();  
 6 
 7     cImage.Destroy(); 
 8     cImage.Create(width,   
 9         height,   
10         8*channels ); 
11 
12     uchar* ps;  
13     uchar* pimg = (uchar*)cImage.GetBits(); 
14     int step = cImage.GetPitch();  
15 
16     for (int i = 0; i < height; ++i)  
17     {  
18         ps = (mat.ptr<uchar>(i));  
19         for ( int j = 0; j < width; ++j )  
20         {  
21             if ( channels == 1 ) //gray  
22             {  
23                 *(pimg + i*step + j) = ps[j];  
24             }  
25             else if ( channels == 3 ) //color  
26             {  
27                 for (int k = 0 ; k < 3; ++k )  
28                 {  
29                     *(pimg + i*step + j*3 + k ) = ps[j*3 + k];  
30                 }             
31             }  
32         }     
33     }  
34 
35 }  
複製代碼

3. 將圖片顯示在窗口DC
複製代碼
1 Mat & image = pDoc->src_image;
2     if (image.empty())
3     {
4         return;
5     }
6     CImage cimage;
7     ImageUtility::MatToCImage(image,cimage);
8     cimage.Draw(pDC->GetSafeHdc(),0,0,cimage.GetWidth(),cimage.GetHeight(),
9         0,0,cimage.GetWidth(),cimage.GetHeight());
複製代碼

 

 終於圖片可以顯示出來了,如下圖:

                                   

 

【fit圖片到窗口大小】

          從上面的結果來看,顯示是顯示出來了,但是效果不好,因爲圖片比較大,超過了窗口大小,所以在繪製時,需要做一個縮放,縮放到適合窗口顯示的大小,縮放之前,需要先得到窗口大小。
 1. override CImageProcessView的OnSize

複製代碼
1 void CImageProcessView::OnSize(UINT nType, int cx, int cy)
2 {
3     nWidth = cx;
4     nHeight = cy;
5     CView::OnSize(nType, cx, cy);
6     // TODO: Add your message handler code here
7 }
複製代碼

 

 

 2. 將圖像縮放到適合窗口顯示的大小

複製代碼
 1 int fixed_width = min(cimage.GetWidth(),nWidth);
 2     int fixed_height = min(cimage.GetHeight(),nHeight);
 3 
 4     double ratio_w = fixed_width / (double)cimage.GetWidth();
 5     double ratio_h = fixed_height / (double)cimage.GetHeight();
 6     double ratio = min(ratio_w,ratio_h);
 7 
 8     int show_width = (int)(ratio * cimage.GetWidth());
 9     int show_height = (int)(ratio * cimage.GetHeight());
10     int offsetx = (nWidth - show_width) / 2;
11     int offsety = (nHeight - show_height) / 2;
12     ::SetStretchBltMode(pDC->GetSafeHdc(),   COLORONCOLOR); 
13     cimage.StretchBlt(pDC->GetSafeHdc(),
14                          offsetx,offsety,
15                       show_width,show_height,0,0,cimage.GetWidth(),cimage.GetHeight(),
16                       SRCCOPY);
複製代碼

 

 

 這些圖片能完整顯示了,而且是顯示在窗口的中間,如圖

                        

 

【雙緩存去閃爍】

當我們resize窗口時,上面的程序會有劇烈的閃動,這誰能受得了了, 爲了改進這一體驗,我們使用雙緩存方案。

1. override CImageProcessView的OnEraseBkgnd
    這樣就不再畫背景畫刷到窗口DC了。

複製代碼
1 BOOL CImageProcessView::OnEraseBkgnd(CDC* pDC)
2 {
3     // TODO: Add your message handler code here and/or call default
4     //return CView::OnEraseBkgnd(pDC);
5     return TRUE;
6 }
複製代碼


2. 加入雙緩存
首先寫一個雙緩存類DoubleBufferSys 

複製代碼
 1 #pragma once
 2 #include <windows.h>
 3 class DoubleBufferSys
 4 {
 5 public:
 6     DoubleBufferSys();
 7     ~DoubleBufferSys();
 8     void Resize(int width,int height);
 9     void SetCDC(CDC * pDC);
10     CDC& GetMemDC();
11     void Present();
12 private:
13     CDC MemDC; //首先定義一個顯示設備對象  
14     CBitmap MemBitmap;//定義一個位圖對象  
15     CDC * pDC;
16     int width;
17     int height;
18 
19 };
複製代碼

 

實現代碼如下
複製代碼
 1 #include "stdafx.h"
 2 #include "DoubleBufferSys.h"
 3 DoubleBufferSys::DoubleBufferSys()
 4 {
 5     MemDC.CreateCompatibleDC(NULL);
 6 }
 7 
 8 DoubleBufferSys::~DoubleBufferSys()
 9 {
10     MemBitmap.DeleteObject();  
11     MemDC.DeleteDC();  
12 }
13 void DoubleBufferSys::Present()
14 {
15     pDC->BitBlt(0,0,width,height,&MemDC,0,0,SRCCOPY);  
16 }
17 void DoubleBufferSys::Resize(int _width,int _height)
18 {
19     if (_width <=0 || _height <=0)
20     {
21         return;
22     }
23     width = _width;
24     height = _height;
25 
26     MemBitmap.DeleteObject();  
27     MemBitmap.CreateCompatibleBitmap(pDC,width,height);  
28     CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap);  
29     MemDC.FillSolidRect(0,0,width,height,RGB(0,0,0));
30 }
31 
32 void DoubleBufferSys::SetCDC(CDC *_pDC)
33 {
34     pDC = _pDC;
35 }
36 
37 CDC& DoubleBufferSys::GetMemDC()
38 {
39     return MemDC;
40 }
複製代碼

然後在CImageProcessView類中定義一個雙緩存系統對象DoubleBufferSys dbbufSys; 並在繪製函數中如下調用

複製代碼
 1 dbbufSys.SetCDC(pDC);
 2 dbbufSys.Resize(nWidth,nHeight);
 3 
 4 Mat & image = pDoc->src_image;
 5 if (image.empty())
 6 {
 7     dbbufSys.Present();
 8     return;
 9 }
10 
11 .................    
12 
13 ::SetStretchBltMode(dbbufSys.GetMemDC(),   COLORONCOLOR); 
14 cimage.StretchBlt(dbbufSys.GetMemDC(),
15           offsetx,offsety,
16           show_width,show_height,0,0,cimage.GetWidth(),cimage.GetHeight(),
17           SRCCOPY);
18 
19 dbbufSys.Present();
複製代碼

 

 這樣就不會出現討厭的閃爍了,另外,DoubleBufferSys這個類可以複用,使用時按照如下流程即可

   1. 設置CDC指針到DoubleBufferSys

   2. Resize 雙緩存大小

   3. 在雙緩存中的緩存中繪製

   4. 將緩存中的內容Present(也就是拷貝到)顯存

 

 

這樣,一個比較完整的利用opencv解碼jpeg,並在窗口中顯示的小程序就完成了,以後可以基於此實現一些數字處理的算法。 

 

 

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