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);
图片进行地雕刻效果如下:
雕刻前:
雕刻后:
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);
}
}
浮雕效果如下:
二、制作底片滤镜
底片滤镜的原理:底片实际上是对色彩进行反转的过程。对于黑白照片,它的原理是白色变成黑色,黑色变成白色。底片滤镜就是用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);
底片滤镜的效果如下:
三、制作油画滤镜
油画的过程可以理解成“堆”颜料的过程,当一种颜料对背景色覆盖不完全或者画笔用力不同时,便可以形成油画特色。算法原理是:
用当前点周围内任一点的颜色来代替当前点的颜色。正是这种随机效果,才实现了色块的形成。
具体的代码表现如下:
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);
木刻效果如下:
四、强光照射
强光照射又叫“局部曝光”。对于强光照射最直接的描述是在暗处打开手电筒照射一幅画,画的中心区域会随之变亮。让画面变亮只需要分别增加每一个像素的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);
强光照射效果如下:
五、柔化与锐化
柔化:就是对图像进行平滑处理,减少相邻像素间的颜色差别,一般选用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);
柔化效果为:
锐化的代码表现为:
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);
锐化的效果如下:
好了到这里GDI+技术在图像滤镜方面的应用就介绍完成了。写下这篇博客记录一下,以备后面复习。
欢迎大家一起交流,学习。
本项目的源代码:Gitee