VC++中GDI+在图像滤镜制作方面的运用

本文详细介绍了如何利用GDI+在Windows图形设备接口的基础上创建各种图像滤镜,如雕刻、浮雕、底片、油画、木刻效果以及强光照射和图像平滑处理(柔化和锐化)。通过像素级差值运算实现滤镜效果,并提供了相应的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

VC++中的GDI+在图像滤镜制作方面的运用

在精通GDI+编程这本书中看到了例子,于是记录学习一下,以备后面复习。关于GDI+的初始化;可以看我博客:MFC使用GDI+来加载PNG,JPG格式的图片 这里不在赘述。

一、制作浮雕及雕刻滤镜

1.雕刻的原理:

对图像上的一个像素和它左上方的那个像素之间的差值的处理过程,处理的结果即是“雕刻”的效果,这种处理是基于像素的。算法是在相邻的像素的差值上加一个常数。在编程过程中只需分别访问图像的每一个像素的RGB值,并将该RGB值与其左上方的像素进行差值运算,便能够轻松的做出"雕刻"的效果。

另外,需要注意的是:设置一个像素时,它和它左上方的像素都需要被用到,为了避免用到已经设置过得像素,应该从图像的右下方的像素开始处理。

具体代码如下:

    CRect rect;
	GetDlgItem(IDC_STATIC_PICTURE)->GetWindowRect(&rect);
	ScreenToClient(&rect);
	Gdiplus::Graphics graphics(dc.m_hDC); // Create a GDI+ graphics object
	CBitmap bmp;
	bmp.LoadBitmapW(IDB_BMP_MYLOVE);
	CPalette palette;
	palette.CreateHalftonePalette(&dc);
	Gdiplus::Bitmap image((HBITMAP)bmp, (HPALETTE)palette); // Construct an image	
	int nWidth = image.GetWidth();
	int nHeight = image.GetHeight();
	Gdiplus::Color color,colorLeft,colorTemp;
    // 核心雕刻处理
	for (int i = nWidth; i > 0; i--)
	{
		for (int j= nHeight;j>0;j--)
		{
			image.GetPixel(i,j, &color);
			image.GetPixel(i-1, j-1, &colorLeft);
			float dbR = max(67,min(255,abs(color.GetRed() - colorLeft.GetRed()+128)));
			float dbG = max(67, min(255, abs(color.GetGreen() - colorLeft.GetGreen() + 128)));
			float dbB = max(67, min(255, abs(color.GetBlue() - colorLeft.GetBlue() + 128)));
			colorTemp.SetValue(color.MakeARGB(255, dbR, dbG, dbB));
			image.SetPixel(i,j,colorTemp);
		}	
	}
	graphics.DrawImage(&image, Rect(rect.left, rect.top, rect.Width(), rect.Height()));
	DeleteDC(dc.m_hDC);
	

图片进行地雕刻效果如下:

雕刻前:

微信截图_20240118235132

雕刻后:

微信截图_20240118235140

2.浮雕的原理:

与雕刻相反。通过获取一个图像和它右下方的像素之间的差值并加上一个常数(如128),来得到浮雕图像。同样,需要注意的是:设置一个像素时,它和它右上方的像素都需要被用到,为了避免用到已经设置过得像素,应该从图像的左上方的像素开始处理。

浮雕具体代码表现如下:

......
// 核心处理代码
for (int i = 0; i < nHeight; i++)
{
	for (int j = 0; j < nWidth-1; j++)
	{
		image.GetPixel(j, i, &color);
		image.GetPixel(j + 1, i + 1, &colorLeft);

		float dbR = max(67, min(255, abs(color.GetRed() - colorLeft.GetRed() + 128)));
		float dbG = max(67, min(255, abs(color.GetGreen() - colorLeft.GetGreen() + 128)));
		float dbB = max(67, min(255, abs(color.GetBlue() - colorLeft.GetBlue() + 128)));

		colorTemp.SetValue(color.MakeARGB(255, dbR, dbG, dbB));

		image.SetPixel(j,i,colorTemp);
	}	
}

浮雕效果如下:

微信截图_20240118235619

二、制作底片滤镜

底片滤镜的原理:底片实际上是对色彩进行反转的过程。对于黑白照片,它的原理是白色变成黑色,黑色变成白色。底片滤镜就是用255-原有的RGB值。

底片滤镜的具体表现如下:

Gdiplus::Color color, colorLeft, colorTemp;
for (int i = 0; i < nHeight; i++)
{
	    for (int j = 0; j < nWidth - 1; j++)
        {
			image.GetPixel(j, i, &color);
			float dbR = 255 - color.GetRed();
			float dbG = 255 - color.GetGreen();
			float dbB = 255 - color.GetBlue();
			colorTemp.SetValue(color.MakeARGB(255, dbR, dbG, dbB));
		    image.SetPixel(j, i, colorTemp);
	    }
}
graphics.DrawImage(&image, Rect(rect.left, rect.top, rect.Width(), rect.Height()));
DeleteDC(dc.m_hDC);

底片滤镜的效果如下:

微信截图_20240121175948

三、制作油画滤镜

油画的过程可以理解成“堆”颜料的过程,当一种颜料对背景色覆盖不完全或者画笔用力不同时,便可以形成油画特色。算法原理是:

用当前点周围内任一点的颜色来代替当前点的颜色。正是这种随机效果,才实现了色块的形成。

具体的代码表现如下:

int nAvg = 0;
		srand((unsigned)time(NULL));
		Gdiplus::Color color, colorLeft, colorTemp;
		for (int i = 0; i < nWidth-5; i++)
		{
			for (int j = 0; j < nHeight - 3; j++)
			{
				int n = rand() % 5;
				image.GetPixel(i+ n, j+n, &color);
				image.SetPixel(i, j, color);
			}
		}
		graphics.DrawImage(&image, Rect(rect.left, rect.top, rect.Width(), rect.Height()));
		DeleteDC(dc.m_hDC);

油画的显示效果如下图:
在这里插入图片描述
三、制作木刻滤镜

木刻滤镜的原理:分析每一个像素的RGB值,如果该点比较亮,将之改为黑色。反之则改为白色。亮与不亮的标准在于该点的RGB值法人平均分量值是否达到255/2;

具体代码表现:

int nTmp = 0;
		Gdiplus::Color color, colorLeft, colorTemp;
		for (int i = 0; i < nWidth; i++)
		{
			for (int j = 0; j < nHeight; j++)
			{
				int  nAvg = 0;
				image.GetPixel(j, i, &color);
				nAvg = (color.GetRed() + color.GetGreen() + color.GetBlue())/3;
				if (nAvg >= 128)
					nTmp = 255;
				else
					nTmp = 0;
				
				colorTemp.SetValue(color.MakeARGB(255, nTmp, nTmp, nTmp));
				image.SetPixel(i,j,colorTemp);
			}
		}
		graphics.DrawImage(&image, Rect(rect.left, rect.top, rect.Width(), rect.Height()));
		DeleteDC(dc.m_hDC);

木刻效果如下:

Snipaste_2024-01-21_22-30-09

四、强光照射

强光照射又叫“局部曝光”。对于强光照射最直接的描述是在暗处打开手电筒照射一幅画,画的中心区域会随之变亮。让画面变亮只需要分别增加每一个像素的RGB值。 这里圆形照射面为例,要判断让哪些像素变亮,主要看像素是否位于照射面内,这一点可以通过像素坐标来判断。比如,像素A的坐标是A(x,y),强光的中心点是C(X,Y),照射面的半径是R,则像素A与中心C之间的关系如下:

①Distance(A,C) > R,像素A在照射面之外。

②Distance(A,C) = R,像素A在照射面的边缘线上。

③Distance(A,C) < R,像素A在照射面之内。

解决了哪些像素的亮问题。还有一个光晕的问题要处理:从光晕的分布来看,光源中心最亮,光强度随半径的增大而减弱,我们可以通过像素点距离光源的中心的距离对RGB值分量加上不同的值。

强光照射具体 的代码表现如下:

int nA = nWidth / 2;
		int nB = nHeight / 2;
		CPoint Centerpt(nA, nB);
		int nRadius = 100;
		Gdiplus::Color color, colorLeft, colorTemp;
		for (int i = 0; i < nWidth; i++)
		{
			for (int j = 0; j < nHeight; j++)
			{
				CPoint pt(i,j);
				if (Distance(pt, Centerpt) < nRadius)
				{
					
					image.GetPixel(i, j, &color);
					float dbR = 0.0, dbG = 0.0,dbB = 0.0;
					dbR = color.GetRed() + 220 * (1 - Distance(pt, Centerpt)/ nRadius);
					dbR = max(0,min(dbR,255));
					dbG = color.GetGreen() + 220 * (1 - Distance(pt, Centerpt) / nRadius);
					dbG = max(0, min(dbG, 255));
					dbB = color.GetBlue() + 220 * (1 - Distance(pt, Centerpt) / nRadius);
					dbB = max(0, min(dbB, 255));

					colorTemp.SetValue(color.MakeARGB(255, dbR, dbG, dbB));
					image.SetPixel(i, j, colorTemp);
				}
			}
		}
		graphics.DrawImage(&image, Rect(rect.left, rect.top, rect.Width(), rect.Height()));
		DeleteDC(dc.m_hDC);

强光照射效果如下:

Snipaste_2024-01-21_22-46-27

五、柔化与锐化

柔化:就是对图像进行平滑处理,减少相邻像素间的颜色差别,一般选用3*3像素块,将中间的像素值改成这9个像素的平均像素值。

锐化:他与柔化操作正好相反,它的目的是突出图像的变化部分,算法是:将要处理的像素与它左对角线的像素之间的差值乘上一个锐化系数,然后再加上原先的像素值。

柔化代码表现如下:

Gdiplus::Color color[3][3], color2, colorTemp;
		for (int i = 1; i < nWidth-2; i++)
		{
			for (int j = 1; j < nHeight-2; j++)
			{
				image.GetPixel(i-1, j-1, &color[0][0]);
				image.GetPixel(i - 1, j, &color[0][1]);
				image.GetPixel(i - 1, j+1, &color[0][2]);

				image.GetPixel(i, j - 1, &color[1][0]);
				image.GetPixel(i, j, &color[1][1]);
				image.GetPixel(i, j + 1, &color[1][2]);

				image.GetPixel(i+1, j - 1, &color[2][0]);
				image.GetPixel(i+1, j, &color[2][1]);
				image.GetPixel(i+1, j + 1, &color[2][2]);

				int nRSum = 0;
				int nGSum = 0;
				int nBSum = 0;

				for (int m = 0; m < 3; m++)
				{
					for (int n = 0; n < 3; n++)
					{
						nRSum += color[m][n].GetRed();
						nGSum += color[m][n].GetGreen();
						nBSum += color[m][n].GetBlue();
					}
				}
				colorTemp.SetValue(color2.MakeARGB(255, (float)(nRSum/9), (float)(nGSum / 9), (float)(nBSum / 9)));
				image.SetPixel(i, j, colorTemp);
				
			}
		}
		graphics.DrawImage(&image, Rect(rect.left, rect.top, rect.Width(), rect.Height()));
		DeleteDC(dc.m_hDC);

柔化效果为:

Snipaste_2024-01-21_23-43-33

锐化的代码表现为:

Gdiplus::Color colorLeft, color3, colorNow, colorTemp;
		float dbDep = 0.990f;
		for (int i = 1; i < nWidth - 1; i++)
		{
			for (int j = 1; j < nHeight - 1; j++)
			{
				image.GetPixel(i, j, &colorNow);
				image.GetPixel(i - 1, j-1, &colorLeft);

				float dbR = colorNow.GetRed() + (colorNow.GetRed() - colorLeft.GetRed() * dbDep);
				dbR = min(255,max(0, dbR));
				float dbG = colorNow.GetGreen() + (colorNow.GetGreen() - colorLeft.GetGreen() * dbDep);
				dbG = min(255, max(0, dbG));
				float dbB = colorNow.GetBlue() + (colorNow.GetBlue() - colorLeft.GetBlue() * dbDep);
				dbB = min(255, max(0, dbB));

				colorTemp.SetValue(color3.MakeARGB(255, dbR, dbG, dbB));
				image.SetPixel(i, j, colorTemp);

			}
		}
		graphics.DrawImage(&image, Rect(rect.left, rect.top, rect.Width(), rect.Height()));
		DeleteDC(dc.m_hDC);

锐化的效果如下:

Snipaste_2024-01-21_23-43-43

好了到这里GDI+技术在图像滤镜方面的应用就介绍完成了。写下这篇博客记录一下,以备后面复习。

欢迎大家一起交流,学习。

本项目的源代码:Gitee

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值