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,并在窗口中显示的小程序就完成了,以后可以基于此实现一些数字处理的算法。 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值