• Opencv (Opencv2)结合MFC学习数字图像处理【3】显示图片


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

  • 相关阅读:
    分页技术-总结
    整合GreyBox放大显示图片
    java web开发 图片上传功能
    我的fckeditor实践
    fckeditor使用(转)
    hibernate主键生成策略(转载)
    JfreeChart使用(转载)
    投票系统开发总结struts2,jfreechart,cookie应用,以及前端技术
    Spring MVC + MyBatis整合(IntelliJ IDEA环境下)
    动态代理
  • 原文地址:https://www.cnblogs.com/speedmancs/p/2675707.html
Copyright © 2020-2023  润新知