基于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的时候还是要出去走一走,回来思路更加清晰一点。要学习的地方还有很多,继续加油!

参考博客:
【基于MFC单文档程序九宫格拼图】_平平无奇隆的博客-CSDN博客

MFC 设置窗口背景图片、设置子窗体背景图片_mfc子窗口显示图片-CSDN博客

posted @ 2023-12-06 16:03  Flying3080  Views(46)  Comments(0Edit  收藏  举报  来源