本文基于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;
返回值:文件的全路径。