(一)利用MFC搭建图片显示的界面

时光总嘲笑我的痴心妄想 提交于 2019-11-28 13:38:50

  本文基于VS2010平台,利用MFC搭建Opencv 2.4.8图像显示的界面。下面两张图分别是图片获取的演示和边缘检测后的图像显示。

  下面是详细流程:

一.搭建MFC对话框


  其中ReadImage是打开图片的按钮,EdgeDetect是边缘检测的按钮,Close按钮用于关闭界面;左边用picture_Control控件显示图片,其ID号重命名为IDC_ShowImg.

二.MFC与Opencv结合

        其实,我们用MFC,主要是用来显示Opencv图形处理后生成的图像,所以图像显示部分用MFC的画笔来完成。那么,点击读取原图的按钮,直接显示原图;点击边缘检测的按钮,则是利用Opencv的边缘检测函数处理原图后,再利用MFC的图像显示函数读取出来。

0)在编写图像处理的函数前需要定义和初始化一些变量

<1>在***.h文件里添加宏定义

1 #define  IMAGE_HEIGHT 450 
2 #define  IMAGE_WIDTH  450
3 #define IMAGE_CHANNELS 3 

<2>在***Dlg.h定义变量

 1 IplImage* TheImage;//代码内部图像变量的指针 

<3>在 ***Dlg.cpp的OnInitDialog()函数里初始化变量

1  CvSize ImgSize; 
2  ImgSize.height = IMAGE_HEIGHT; 
3  ImgSize.width = IMAGE_WIDTH; 
4  TheImage = cvCreateImage(ImgSize, IPL_DEPTH_8U, IMAGE_CHANNELS);//TheImage变量的初始化

<4>在***Dlg.cpp的OPaint()函数的else()函数里添加:

1     else
2     {
3         CDialogEx::OnPaint();//重绘对话框
4     //    CDialog::OnPaint(); 
5         CDialog::UpdateWindow(); //更新windows窗口
6         ShowImage(TheImage , IDC_ShowImg); //图像在picturc控件上显示
7     }

    接下来就是一些核心函数的编写了:

1)添加Opencv的头文件函数,可以放在test01.h中,也可以放在stdafx.h中

1 #include <opencv2/core/core.hpp>  
2 #include <opencv2/highgui/highgui.hpp>
3 #include<opencv2\opencv.hpp>

2)编写读入图形的部分,双击ReadImage按钮,程序转到void C***Dlg::OnBnClickedButton1(),在此处添加代码:(****文末附有相关函数的解释***)

 1 void Ctest01Dlg::OnBnClickedButton1()
 2 {
 3     // TODO: 在此添加控件通知处理程序代码
 4     CFileDialog dlg(true, _T("*.bmp"), NULL, OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
 5 
 6         _T("image files (*.bmp ;*.jpg)|*.bmp; *.jpg |ALL Files (*.*) |*.*||"),NULL); //调用此成员函数构造一个标准Windows文件对话框对象
 7 
 8     //打开文件对话框的标题名
 9     dlg.m_ofn.lpstrTitle = _T("open Image "); //打开的对话框命名为“open Image”
10     if (dlg.DoModal() != IDOK)//判断是否选取了图片
11         return; 
12     CString  mPath = dlg.GetPathName(); //获得图片的路径
13     //读取图片 缓存到一个局部变量 ipl 中 
14     
15     IplImage * ipl = cvLoadImage(CT2CA(  mPath), 1);//读取图片,缓存至局部变量ipl
16     if (!ipl) return; //如果缓存失败,返回
17     if (TheImage)//清空TheImage变量里的数据
18         cvZero(TheImage); 
19         ResizeImage(ipl); //调整ipl图片的大小并复制到TheImage中
20         ShowImage(TheImage, IDC_ShowImg); //在picture控件中显示图片
21         cvReleaseImage(&ipl); //释放局部变量
22 }

3)添加ResizeImage(IplImage* img)和ShowImage(IplImage* image, UINT ID)函数

    <1> 在C***Dlg.h文件里面添加函数声明

1 void ShowImage(IplImage* image, UINT ID); 2 void ResizeImage(IplImage* img);  

   <2>在C***Dlg.cpp文件里面添加函数实现

 1 void Ctest01Dlg::ResizeImage(IplImage* img)
 2 {
 3     int w = img->width;//获取原图片的宽和高
 4     int h = img->height; 
 5     int max = (w > h) ? w : h;//选取宽和高的最大值赋给max
 6     float  scale = (float)((float)max / 450.0f);//计算缩放因子
 7     int nw = (int)(w / scale);//计算缩放后的图片的宽和高
 8     int nh = (int)(h / scale); 
 9     int tlx = (nw > nh) ? 0 : (int)(450 - nw) / 2;//为保证图片放在TheImage的正中间,计算左上角的坐标
10     int tly = (nw > nh) ? (int)(450 - nh) / 2 : 0;
11     cvSetImageROI( TheImage , cvRect(tlx, tly, nw, nh));//选取TheImage的ROI区域
12     cvResize(img, TheImage);//将原来的图片调整大小后复制给TheImage的ROI区域
13     cvResetImageROI(TheImage);//重置ROI区域
14 }
 1 void  Ctest01Dlg:: ShowImage(IplImage* image, UINT ID)
 2 {
 3     CDC *pDC = GetDlgItem(ID)->GetDC(); //获得picture控件的DC
 4     HDC hDC = pDC->GetSafeHdc();//获取设备句柄进行绘图操作
 5     CRect rect; 
 6     GetDlgItem(ID)->GetClientRect(&rect);//获取picture控件的坐标位置
 7     int rw = rect.right - rect.left;//获取pictur控件的宽和高
 8     int rh = rect.bottom - rect.top;
 9     int iw = image->width; //获取image(TheImage)的宽和高
10     int ih = image->height;
11     int tx = (int)(rw - iw) / 2; //为保证image(TheImage)放在picture控件的正中间,计算左上角的坐标
12     int ty = (int)(rh - ih) / 2;
13     SetRect(rect, tx, ty, tx + iw, ty + ih);//设置image(TheImage)被放置的位置
14     CvvImage cimg; 
15     cimg.CopyOf(image);//复制图片
16     cimg.DrawToHDC(hDC, &rect);//将图片绘制到显示控件的指定区域
17     ReleaseDC(pDC);//释放
18 }

 4)编写图像边缘检测部分的代码,双击EdgeImage按钮,程序转到void C***Dlg::OnBnClickedButton2(),在此处添加代码:

 1 void Ctest01Dlg::OnBnClickedButton2()
 2 {
 3         IplImage *gray = 0, *edge = 0;//定义灰度图和边缘检测图的指针变量
 4         gray = cvCreateImage(cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 1); //初始化指针变量    
 5         edge = cvCreateImage(cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 1);    
 6         cvCvtColor(TheImage, gray, CV_BGR2GRAY);  //图像灰度化   
 7         cvCanny(gray, edge, 30, 100, 3); //用canny算子进行图像边缘检测   
 8         cvCvtColor(edge, TheImage, CV_GRAY2BGR);  //灰度图转换为RGB图
 9         ShowImage(TheImage, IDC_ShowImg); //在picture控件上显示图像    
10         cvReleaseImage(&gray);    //释放局部变量
11         cvReleaseImage(&edge);
12     // TODO: 在此添加控件通知处理程序代码
13 }

5)至此,代码基本完成,编译运行后应该没什么问题。不过,有的人可能会出现IpIImagle等类型无法识别的问题,这是由于OpenCV版本的更新,高版本中不再使用CvvImage类显示图片,不过我们可以通过自己手动添加打方式加入工程文件中。在项目的头文件和源文件中分别添加CvvImage.h和CvvImage.cpp:

//CvvImage.h 1 #ifndef CVVIMAGE_CLASS_DEF
 2 #define CVVIMAGE_CLASS_DEF
 3 
 4 #include <opencv/cv.h>
 5 #include <opencv/highgui.h>
 6 
 7 /* CvvImage class definition */
 8 class CvvImage
 9 {
10 public:
11     CvvImage();
12     virtual ~CvvImage();
13 
14     /* Create image (BGR or grayscale) */
15     virtual bool Create(int width, int height, int bits_per_pixel, int image_origin = 0);
16 
17     /* Load image from specified file */
18     virtual bool Load(const char* filename, int desired_color = 1);
19 
20     /* Load rectangle from the file */
21     virtual bool LoadRect(const char* filename,
22         int desired_color, CvRect r);
23 
24 #if defined WIN32 || defined _WIN32
25     virtual bool LoadRect(const char* filename,
26         int desired_color, RECT r)
27     {
28         return LoadRect(filename, desired_color,
29             cvRect(r.left, r.top, r.right - r.left, r.bottom - r.top));
30     }
31 #endif
32 
33     /* Save entire image to specified file. */
34     virtual bool Save(const char* filename);
35 
36     /* Get copy of input image ROI */
37     virtual void CopyOf(CvvImage& image, int desired_color = -1);
38     virtual void CopyOf(IplImage* img, int desired_color = -1);
39 
40     IplImage* GetImage() { return m_img; };
41     virtual void Destroy(void);
42 
43     /* width and height of ROI */
44     int Width() { return !m_img ? 0 : !m_img->roi ? m_img->width : m_img->roi->width; };
45     int Height() { return !m_img ? 0 : !m_img->roi ? m_img->height : m_img->roi->height; };
46     int Bpp() { return m_img ? (m_img->depth & 255)*m_img->nChannels : 0; };
47 
48     virtual void Fill(int color);
49 
50     /* draw to highgui window */
51     virtual void Show(const char* window);
52 
53 #if defined WIN32 || defined _WIN32
54     /* draw part of image to the specified DC */
55     virtual void Show( HDC dc, int x, int y, int width, int height,
56         int from_x = 0, int from_y = 0);
57     /* draw the current image ROI to the specified rectangle of the destination DC */
58     virtual void DrawToHDC( HDC hDCDst, RECT* pDstRect);
59 #endif
60 
61 protected:
62 
63     IplImage* m_img;
64 };
65 
66 typedef CvvImage CImage;
67 
68 #endif
  //CvvImage.cpp  1 #include "stdafx.h"//加到工程后这个就要添上
  2 #include "CvvImage.h"
  3 
  4 //////////////////////////////////////////////////////////////////////
  5 // Construction/Destruction
  6 //////////////////////////////////////////////////////////////////////
  7 
  8 CV_INLINE RECT NormalizeRect(RECT r);
  9 CV_INLINE RECT NormalizeRect(RECT r)
 10 {
 11     int t;
 12 
 13     if (r.left > r.right)
 14     {
 15         t = r.left;
 16         r.left = r.right;
 17         r.right = t;
 18     }
 19 
 20     if (r.top > r.bottom)
 21     {
 22         t = r.top;
 23         r.top = r.bottom;
 24         r.bottom = t;
 25     }
 26 
 27     return r;
 28 }
 29 
 30 CV_INLINE CvRect RectToCvRect(RECT sr);
 31 CV_INLINE CvRect RectToCvRect(RECT sr)
 32 {
 33     sr = NormalizeRect(sr);
 34     return cvRect(sr.left, sr.top, sr.right - sr.left, sr.bottom - sr.top);
 35 }
 36 
 37 CV_INLINE RECT CvRectToRect(CvRect sr);
 38 CV_INLINE RECT CvRectToRect(CvRect sr)
 39 {
 40     RECT dr;
 41     dr.left = sr.x;
 42     dr.top = sr.y;
 43     dr.right = sr.x + sr.width;
 44     dr.bottom = sr.y + sr.height;
 45 
 46     return dr;
 47 }
 48 
 49 CV_INLINE IplROI RectToROI(RECT r);
 50 CV_INLINE IplROI RectToROI(RECT r)
 51 {
 52     IplROI roi;
 53     r = NormalizeRect(r);
 54     roi.xOffset = r.left;
 55     roi.yOffset = r.top;
 56     roi.width = r.right - r.left;
 57     roi.height = r.bottom - r.top;
 58     roi.coi = 0;
 59 
 60     return roi;
 61 }
 62 
 63 void FillBitmapInfo(BITMAPINFO* bmi, int width, int height, int bpp, int origin)
 64 {
 65     assert(bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32));
 66 
 67     BITMAPINFOHEADER* bmih = &(bmi->bmiHeader);
 68 
 69     memset(bmih, 0, sizeof(*bmih));
 70     bmih->biSize = sizeof(BITMAPINFOHEADER);
 71     bmih->biWidth = width;
 72     bmih->biHeight = origin ? abs(height) : -abs(height);
 73     bmih->biPlanes = 1;
 74     bmih->biBitCount = (unsigned short)bpp;
 75     bmih->biCompression = BI_RGB;
 76 
 77     if (bpp == 8)
 78     {
 79         RGBQUAD* palette = bmi->bmiColors;
 80         int i;
 81         for (i = 0; i < 256; i++)
 82         {
 83             palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i;
 84             palette[i].rgbReserved = 0;
 85         }
 86     }
 87 }
 88 
 89 CvvImage::CvvImage()
 90 {
 91     m_img = 0;
 92 }
 93 
 94 void CvvImage::Destroy()
 95 {
 96     cvReleaseImage(&m_img);
 97 }
 98 
 99 CvvImage::~CvvImage()
100 {
101     Destroy();
102 }
103 
104 bool CvvImage::Create(int w, int h, int bpp, int origin)
105 {
106     const unsigned max_img_size = 10000;
107 
108     if ((bpp != 8 && bpp != 24 && bpp != 32) ||
109         (unsigned)w >= max_img_size || (unsigned)h >= max_img_size ||
110         (origin != IPL_ORIGIN_TL && origin != IPL_ORIGIN_BL))
111     {
112         assert(0); // most probably, it is a programming error
113         return false;
114     }
115 
116     if (!m_img || Bpp() != bpp || m_img->width != w || m_img->height != h)
117     {
118         if (m_img && m_img->nSize == sizeof(IplImage))
119             Destroy();
120 
121         /* prepare IPL header */
122         m_img = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, bpp / 8);
123     }
124 
125     if (m_img)
126         m_img->origin = origin == 0 ? IPL_ORIGIN_TL : IPL_ORIGIN_BL;
127 
128     return m_img != 0;
129 }
130 
131 void CvvImage::CopyOf(CvvImage& image, int desired_color)
132 {
133     IplImage* img = image.GetImage();
134     if (img)
135     {
136         CopyOf(img, desired_color);
137     }
138 }
139 
140 
141 #define HG_IS_IMAGE(img) \
142     ((img) != 0 && ((const IplImage*)(img))->nSize == sizeof(IplImage) && \
143     ((IplImage*)img)->imageData != 0)
144 
145 
146 void CvvImage::CopyOf(IplImage* img, int desired_color)
147 {
148     if (HG_IS_IMAGE(img))
149     {
150         int color = desired_color;
151         CvSize size = cvGetSize(img);
152 
153         if (color < 0)
154             color = img->nChannels > 1;
155 
156         if (Create(size.width, size.height,
157             (!color ? 1 : img->nChannels > 1 ? img->nChannels : 3) * 8,
158             img->origin))
159         {
160             cvConvertImage(img, m_img, 0);
161         }
162     }
163 }
164 
165 
166 bool CvvImage::Load(const char* filename, int desired_color)
167 {
168     IplImage* img = cvLoadImage(filename, desired_color);
169     if (!img)
170         return false;
171 
172     CopyOf(img, desired_color);
173     cvReleaseImage(&img);
174 
175     return true;
176 }
177 
178 
179 bool CvvImage::LoadRect(const char* filename,
180     int desired_color, CvRect r)
181 {
182     if (r.width < 0 || r.height < 0) return false;
183 
184     IplImage* img = cvLoadImage(filename, desired_color);
185     if (!img)
186         return false;
187 
188     if (r.width == 0 || r.height == 0)
189     {
190         r.width = img->width;
191         r.height = img->height;
192         r.x = r.y = 0;
193     }
194 
195     if (r.x > img->width || r.y > img->height ||
196         r.x + r.width < 0 || r.y + r.height < 0)
197     {
198         cvReleaseImage(&img);
199         return false;
200     }
201 
202     /* truncate r to source image */
203     if (r.x < 0)
204     {
205         r.width += r.x;
206         r.x = 0;
207     }
208     if (r.y < 0)
209     {
210         r.height += r.y;
211         r.y = 0;
212     }
213 
214     if (r.x + r.width > img->width)
215         r.width = img->width - r.x;
216 
217     if (r.y + r.height > img->height)
218         r.height = img->height - r.y;
219 
220     cvSetImageROI(img, r);
221     CopyOf(img, desired_color);
222 
223     cvReleaseImage(&img);
224     return true;
225 }
226 
227 
228 bool CvvImage::Save(const char* filename)
229 {
230     if (!m_img)
231         return false;
232     cvSaveImage(filename, m_img);
233     return true;
234 }
235 
236 
237 void CvvImage::Show(const char* window)
238 {
239     if (m_img)
240         cvShowImage(window, m_img);
241 }
242 
243 
244 void CvvImage::Show(HDC dc, int x, int y, int w, int h, int from_x, int from_y)
245 {
246     if (m_img && m_img->depth == IPL_DEPTH_8U)
247     {
248         uchar buffer[sizeof(BITMAPINFOHEADER) + 1024];
249         BITMAPINFO* bmi = (BITMAPINFO*)buffer;
250         int bmp_w = m_img->width, bmp_h = m_img->height;
251 
252         FillBitmapInfo(bmi, bmp_w, bmp_h, Bpp(), m_img->origin);
253 
254         from_x = MIN(MAX(from_x, 0), bmp_w - 1);
255         from_y = MIN(MAX(from_y, 0), bmp_h - 1);
256 
257         int sw = MAX(MIN(bmp_w - from_x, w), 0);
258         int sh = MAX(MIN(bmp_h - from_y, h), 0);
259 
260         SetDIBitsToDevice(
261             dc, x, y, sw, sh, from_x, from_y, from_y, sh,
262             m_img->imageData + from_y*m_img->widthStep,
263             bmi, DIB_RGB_COLORS);
264     }
265 }
266 
267 
268 void CvvImage::DrawToHDC(HDC hDCDst, RECT* pDstRect)
269 {
270     if (pDstRect && m_img && m_img->depth == IPL_DEPTH_8U && m_img->imageData)
271     {
272         uchar buffer[sizeof(BITMAPINFOHEADER) + 1024];
273         BITMAPINFO* bmi = (BITMAPINFO*)buffer;
274         int bmp_w = m_img->width, bmp_h = m_img->height;
275 
276         CvRect roi = cvGetImageROI(m_img);
277         CvRect dst = RectToCvRect(*pDstRect);
278 
279         if (roi.width == dst.width && roi.height == dst.height)
280         {
281             Show(hDCDst, dst.x, dst.y, dst.width, dst.height, roi.x, roi.y);
282             return;
283         }
284 
285         if (roi.width > dst.width)
286         {
287             SetStretchBltMode(
288                 hDCDst, // handle to device context
289                 HALFTONE);
290         }
291         else
292         {
293             SetStretchBltMode(
294                 hDCDst, // handle to device context
295                 COLORONCOLOR);
296         }
297 
298         FillBitmapInfo(bmi, bmp_w, bmp_h, Bpp(), m_img->origin);
299 
300         ::StretchDIBits(
301             hDCDst,
302             dst.x, dst.y, dst.width, dst.height,
303             roi.x, roi.y, roi.width, roi.height,
304             m_img->imageData, bmi, DIB_RGB_COLORS, SRCCOPY);
305     }
306 }
307 
308 
309 void CvvImage::Fill(int color)
310 {
311     cvSet(m_img, cvScalar(color & 255, (color >> 8) & 255, (color >> 16) & 255, (color >> 24) & 255));
312 }

  添加之后编译运行应该没问题了。以上就是我利用MFC搭建Opencv图像显示界面的流程。由于本人也是刚开始这方面学习的新手,希望通过这种方式记录自己的学习流程的同时,也能分享给大家我的经验。如果有表述问题或是知识点理解的错误,请大家提出建议,希望我们可以一起学习,一起进步。本文参考的文章有:

1.https://blog.csdn.net/chenyusiyuan/article/details/4744097

2.https://blog.csdn.net/qq_34609108/article/details/78325623

附:一些函数的解释

1.CFileDialog(BOOL bOpenFileDialog,
            LPCTSTR lpszDefExt = NULL,
            LPCTSTR lpszFileName = NULL,
            DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
            LPCTSTR lpszFilter = NULL,
            CWnd* pParentWnd = NULL
           );

    参数:

bOpenFileDialg 如果为TRUE, 则创建文件打开对话框;如果为FALSE,则构造一个File Save As(另存为)对话框。
lpszDefExt 缺省文件扩展名,如果用户在文件名编辑框中不包含扩展名,则lpszDefExt定义的扩展名自动加到文件名后。如果为NULL,则不添加扩展名。
lpszFileName 初始显示于文件名编辑框中的文件名,如果为NULL,则不显示初始文件名。
dwFlags 一个或多个标志的组合,使你可定制对话框。要了解这些标志的描述,可参阅联机文档“Win32 SDK”中的OPENFILENAME结构。如果你改变m_ofn.Flags结构成员,在改变中用OR操作保持缺省行为完整。
lpszFilter 一列字符串对,指定可以应用到文件的过滤器。如果指定过滤器,仅被选择的文件显示于文件列表框中。请参阅说明部分,了解如何使用过滤器。
pParentWnd 指向文件对话框对象的父窗口或拥有者窗口。

2.CFileDialog::m_ofn
        说明:m_ofn是OPENFILENAME类型的结构。使用此结构,在创建之后、用DoModal成员函数显示之前初始化文件打开和存盘对话框的外表。例如,可设置m_ofn的lpstrTitle成员为希望的对话框标题。

3.CFileDialog::DoModal-----virtual int DoModal( );

返回值:
IDOK或IDCANCEL。如果返回IDCANCEL,调用CommDlgExtendedError函数来判断是否是发生错误。
IDOK或IDCANCEL是表明用户选择了OK还是Cancel按钮的常数。

4.CFileDialog::GetPathName-----CString GetPathName( )const;

返回值:文件的全路径。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!