本文对滤镜中常见的晕影,晕角效果的实现做了研究,具体如下:
1 晕影调整算法
所谓晕影就是给图像四个角添加暗影效果,这暗影向图像中心逐渐淡化。我们使用如下公式来实现:
假设图像宽度高度分别为w,h:
double d = Math.Sqrt((i - w / 2) * (i - w / 2) + (j - h / 2) * (j - h / 2));
double dmax = 1.0 / Math.Sqrt(w * w / 4 + h * h / 4);
double lum = 0.75 / (1.0 + Math.Exp((d * dmax - cRadius) * cGradient)) + cLight;
//eg:double lum = 0.75 / (1.0 + Math.Exp((d * dmax - 0.85) * 5)) + 0.35;
r = (int)((double)r * lum);
g = (int)((double)g * lum);
b = (int)((double)b * lum);
注释:
cRadius (cRadius=0.73)这个参数控制晕影的半径,值越大,半径越大;
cLight (cLight=0.25)控制中心圆形区域内的像素亮度,值越大,图像越亮;
cGradient (cGradient=20)这个参数控制中心区域与晕影过渡部分的梯度,值越小,过渡越自然,越不明显;
上述算法实现的是圆形晕影。
有了晕影图像之后,我们采用K比例混合算法来实现晕影强度的可调性,假设晕影图像为S(n,m),原图为X(n,m),效果图为Y(n,m),晕影强度因子为K,K属于[0,1],混合公式如下:
Y(n,m)=K*X(n,m)+(1-K)*S(n,m)
这样就实现了晕影效果从无到有的调节过程。
2 晕影调节快速算法
晕影调节的快速算法主要是改进晕影图像的生成过程。我们可以按照自己的需求使用PS制作出一张晕影效果图,来代替算法生成晕影图像的过程,这样就可以节省算法生成的时间了,之后的调节过程不变。
当然,这个算法的缺点就是晕影的半径,过渡程度和亮度无法调整。
目前,这里给出Instagram里使用的晕角模板:
在Instagram中,是使用这个模板与目标图像执行“柔光”混合图层算法,得到晕角效果的,然后使用1中的K比例混合算法,来快速调节。
最后给出android 中C代码,可以直接使用。
附录 Android C Code with RGB565
void THaloEffect(int* srcData, int width, int height) {
int gray = 0, green = 0, blue = 0, red = 0, i = 0, j = 0, alpha = 0xFF << 24;
for(j = 0; j < height; j++)
{
for(i = 0; i < width; i++)
{
gray = srcData[i + j * width];
red = ((gray >> 16) & 0xFF);
green = ((gray >> 8) & 0xFF);
blue = (gray & 0xFF);
double d = sqrt((i - width / 2) * (i - width / 2) + (j - height / 2) * (j - height / 2));
double dmax = 1.0 / sqrt(width * width / 4 + height * height / 4);
//double lum = 0.75 / (1.0 + Math.Exp((d * dmax - 0.73) * 20)) + 0.25;
double lum = 0.75 / (1.0 + exp((d * dmax - 0.73) * 20)) + 0.25;
red = (int)((double)red * lum);
red = CheckRange(red);
green = (int)((double)green * lum);
green = CheckRange(green);
blue = (int)((double)blue * lum);
blue = CheckRange(blue);
srcData[i + j * width] = alpha | (red << 16) | (green << 8) | blue;
}
}
};
void THaloEffect_F(int* srcData, int* mask, int width, int height, int ratio)
{
int gray = 0,green = 0,blue = 0,red = 0,i = 0, mRed = 0, mGreen = 0, mBlue = 0, alpha = 0xFF << 24;
int length = width * height;
for (i = 0; i < length; i++) {
gray = srcData[i];
red = ((gray >> 16) & 0xFF);
green = ((gray >> 8) & 0xFF);
blue = (gray & 0xFF);
gray = mask[i];
mRed = ((gray >> 16) & 0xFF);
mGreen = ((gray >> 8) & 0xFF);
mBlue = (gray & 0xFF);
mRed = ModeSmoothLight(red, mRed);
mGreen = ModeSmoothLight(green, mGreen);
mBlue = ModeSmoothLight(blue, mBlue);
red = (mRed * ratio + (100 - ratio) * red) / 100;
green = (mGreen * ratio + (100 - ratio) * green) / 100;
blue = (mBlue * ratio + (100 - ratio) * blue) / 100;
srcData[i] = alpha | (red << 16) | (green << 8) | blue;
}
};