基于MFC单文档数字图像处理系统
此数字图像处理系统是基于mfc单文档的,想着做一点花里胡哨的效果出来,比如简单给窗口加个背景,按钮啥的。却发现这种操作网上大部分都是基于对话框的教程,或者就是没有附图,看起来有点吃力。所以简单记录一下这些操作基于单文档如何实现。
一、给窗口添加背景
1、背景图片为bmp格式
2、右击资源文件,点击添加,点击资源
3、选择Bitmap,选择导入
4、导入成功后,一方面:在“解决方案资源管理器”中的资源文件中,可以看到背景图片。另一方面:在“资源视图”的Bitmap中可以看到名为IDB_BITMAP1的图片
记住IDB_BITMAP1,这是你这张背景图片的id。
5、代码
在 MFC 中,OnDraw
函数是用于绘制窗口内容的关键函数之一。这个函数在窗口需要重新绘制时被调用,比如在窗口大小改变、窗口被激活或者用户强制刷新时。因此,在 OnDraw
函数中添加背景图片可以确保背景在窗口被绘制时始终显示。
背景图片通常被绘制在窗口的底层,这样其他绘图操作就可以在其上进行,从而形成窗口的层次结构。
void Ctest3View::OnDraw(CDC* pDC)
{
Ctest3Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
// 获取窗口的客户区大小
CRect rect;
GetClientRect(&rect);
/************************************显示背景图片*****************************/
// 创建兼容内存DC
CDC dcMem;
dcMem.CreateCompatibleDC(pDC);
// 加载背景图片
CBitmap bmpBackground;
if (!bmpBackground.LoadBitmap(IDB_BITMAP1)) {
AfxMessageBox(_T("加载背景图片失败"));
return;
}
//获取位图信息
BITMAP bitMap;
bmpBackground.GetBitmap(&bitMap);
// 选择背景图片到内存设备上下文
CBitmap* pbmpOld = dcMem.SelectObject(&bmpBackground);
if (pbmpOld == nullptr) {
// 处理选择对象失败的情况
AfxMessageBox(_T("选择背景图片失败"));
return;
}
// 绘制背景图片
pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, bitMap.bmWidth, bitMap.bmHeight, SRCCOPY);
// 还原设备上下文
dcMem.SelectObject(pbmpOld);
// 释放位图资源
bmpBackground.DeleteObject();
}
二、添加按钮
1、在Resource.h头文件中添加按钮的资源标识符
以ID号来唯一标识一个按钮,ID号后面跟的数字没有特定的说法,只要互不相同就行。
2、添加OnCreate 函数
点击菜单栏的”项目“------>点击”类向导“,接下来按图示操作:
5、在OnCreat函数里面添加按钮显示代码
其中,按钮的ID号,一定要和Resource.h头文件中的ID一一对应。否则按钮不会显示。
int Ctest3View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
/*修改方法:1、新创建CButton对象,即MyButton
2、新创建rect_button
3、修改坐标,按钮一列排放,修改2、4参数就可以,均加100
4、在Create函数中对应修改按钮文本、rect_button、按钮的ID号*/
/**********************************************反转按钮*********************************************************************/
//1、创建按钮
CButton* MyButton = new CButton(); //创建了一个CButton对象
CRect rect_button(150, 300, 300, 350); //左上角位置开始,逆时针每一位的坐标
MyButton->Create(L"反转变换", WS_CHILD | WS_VISIBLE | WS_BORDER, rect_button, this, ID_Invert);
//2、显示按钮
MyButton->ShowWindow(SW_SHOWNORMAL); // 调用ShowWindow函数显示按钮。
/*********************************************对数变换**********************************************************************/
//1、创建按钮
CButton* MyButton1 = new CButton(); //创建了一个CButton对象
CRect rect_button1(150, 400, 300, 450); //左上角位置开始,逆时针每一位的坐标
MyButton1->Create(L"对数变换", WS_CHILD | WS_VISIBLE | WS_BORDER, rect_button1, this, ID_LogTransform);
//2、显示按钮
MyButton1->ShowWindow(SW_SHOWNORMAL); // 调用ShowWindow函数显示按钮。
/********************************************幂律变换***********************************************************************/
//1、创建按钮
CButton* MyButton2 = new CButton(); //创建了一个CButton对象
CRect rect_button2(150, 500, 300, 550); //左上角位置开始,逆时针每一位的坐标
MyButton2->Create(L"幂律变换", WS_CHILD | WS_VISIBLE | WS_BORDER, rect_button2, this, ID_PowerLawTransform);
//2、显示按钮
MyButton2->ShowWindow(SW_SHOWNORMAL); // 调用ShowWindow函数显示按钮。
/********************************************对比度拉伸*********************************************************************/
//1、创建按钮
CButton* MyButton3 = new CButton(); //创建了一个CButton对象
CRect rect_button3(150, 600, 300, 650); //左上角位置开始,逆时针每一位的坐标
MyButton3->Create(L"对比度拉伸", WS_CHILD | WS_VISIBLE | WS_BORDER, rect_button3, this, ID_ContrastStretching);
//2、显示按钮
MyButton3->ShowWindow(SW_SHOWNORMAL); // 调用ShowWindow函数显示按钮。
/********************************************均值滤波**********************************************************************/
//1、创建按钮
CButton* MyButton4 = new CButton(); //创建了一个CButton对象
CRect rect_button4(150, 700, 300, 750); //左上角位置开始,逆时针每一位的坐标
MyButton4->Create(L"均值滤波", WS_CHILD | WS_VISIBLE | WS_BORDER, rect_button4, this, ID_MeanFilter);
//2、显示按钮
MyButton4->ShowWindow(SW_SHOWNORMAL); // 调用ShowWindow函数显示按钮。
/*******************************************中值滤波***********************************************************************/
//1、创建按钮
CButton* MyButton5 = new CButton(); //创建了一个CButton对象
CRect rect_button5(150, 800, 300, 850); //左上角位置开始,逆时针每一位的坐标
MyButton5->Create(L"中值滤波", WS_CHILD | WS_VISIBLE | WS_BORDER, rect_button5, this, ID_MedianFilter);
//2、显示按钮
MyButton5->ShowWindow(SW_SHOWNORMAL); // 调用ShowWindow函数显示按钮。
//RedrawWindow();
return 0;
}
6、添加按钮响应函数
如今按钮已经可以正常显示在窗口中了,但是现在点击按钮不会有任何的反应,是因为我们还没有给按钮添加消息响应函数。
6.1 在CxxxView.h头文件中声明响应函数
在 “//生成的消息映射函数” 下的public里面进行声明
6.2 在CxxxView.cpp中定义宏,关联按钮和点击事件
ON_BN_CLICKED(A, B)
表示当ID为 A 的按钮被点击时,将触发 B 函数。宏定义建立了消息映射关系,告诉 MFC 框架当用户点击了某个按钮时应该调用哪个函数来处理相应的事件。
注意在BEGIN_MESSAGE_MAP……END_MESSAGE_MAP()中添加宏定义。第一个参数为按钮的ID号,和和Resource.h头文件中的ID一一对应,第二个参数为在CxxxView.h头文件中定义的响应函数函数名。
6.3 在CxxxView.cpp中编写具体的按钮响应函数
//反转图像按钮响应函数
void Ctest3View::OnInvert()
{
CBmp* pBMP = new CBmp();
//要绘制的图像文件名
//左上角坐标
int offset_left = 400;
int offset_top = 300;
char bmpName[] = "2.bmp";
// 显示原图
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left, offset_top);
// 显示图像反转后的图片
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left + 700, offset_top, true, false, false);
}
//对数变换按钮响应函数
void Ctest3View::OnLogTransform()
{
CBmp* pBMP = new CBmp();
//要绘制的图像文件名
//左上角坐标
int offset_left = 1800;
int offset_top = 300;
char bmpName[] = "3.bmp";
// 显示原图
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left, offset_top);
// 显示对数变换后的图片
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left + 750, offset_top, false, true);
}
//幂律变换按钮响应函数
void Ctest3View::OnPowerLawTransform()
{
CBmp* pBMP = new CBmp();
//要绘制的图像文件名
//左上角坐标
int offset_left = 400;
int offset_top = 1050;
char bmpName[] = "4.bmp";
// 显示原图
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left, offset_top);
// 显示幂变换之后的图片
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left + 700, offset_top, false, false, true);
}
//对比度拉伸
void Ctest3View::OnContrastStretching()
{
CBmp* pBMP = new CBmp();
//要绘制的图像文件名
//左上角坐标
int offset_left = 1800;
int offset_top = 1050;
char bmpName[] = "5.bmp";
// 显示原图
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left, offset_top);
// 显示对比度拉伸后的图片
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left + 700, offset_top, false, false, false, 0.0, true);
// 强制重绘窗口
RedrawWindow();
}
//均值滤波
void Ctest3View::OnMeanFilter()
{
CBmp* pBMP = new CBmp();
//要绘制的图像文件名
//左上角坐标
int offset_left = 400;
int offset_top = 300;
char bmpName[] = "6.bmp";
// 显示原图
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left, offset_top);
// 显示均值滤波后的图片
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left + 450, offset_top, false, false, false, 0.0, false, true);
}
//中值滤波
void Ctest3View::OnMedianFilter()
{
CBmp* pBMP = new CBmp();
//要绘制的图像文件名
//左上角坐标
int offset_left = 1300;
int offset_top = 300;
char bmpName[] = "7.bmp";
// 显示原图
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left, offset_top);
// 显示中值滤波后的图片
pBMP->readAndDrawBMP(GetDC(), bmpName, offset_left + 620, offset_top, false, false, false, 0.0, false, false, true);
}
为了让CxxxView.cpp中的程序没有那么冗余,我新建了Bmp.c和Bmp.h,把所有图像处理的代码都放在CBmp类里面,所以按钮响应函数里面调用了readAndDrawBMP函数。具体的图像处理代码在专栏里面的其他博客有体现。
三、最终效果
工程还有许多不完善的地方,比如对比度拉伸的分段函数没有选好,使得效果非常不好。图像处理变换的种类还不够丰富,不过课程还未结束,接下来可能会添加更多数字图像处理的方法。幸好框架搭好了,接下来只需要往对应的地方填代码就行。有些地方卡了好久,如今顺下来这篇博客也只花了一个小时的时间。哎,卡bug的时候还是要出去走一走,回来思路更加清晰一点。要学习的地方还有很多,继续加油!