时间:2017年7月2日(大三下学期)
写在前面:
最近觉得Steam的软件界面非常的美观,尤其是图片的渐隐、雾化效果看起来非常漂亮。无奈我PS技术不行,手笨做不出那样的效果…… 于是,我就自己写了一款方便高效的图像处理程序,只需设置相应的参数即可完成处理(懒人模式)。如果点入的读者只是为了使用程序,那么可以无视我下面啰啰嗦嗦的科普,通过下面的链接,就获取到我的打包程序:
http://download.csdn.net/detail/mahabharata_/9886497
在正文开始之前,首先贴一下效果图:
(1) 程序界面效果图
(2) 图片的处理效果图:
【原理一】 图像雾化 —— Perlin Noise (柏林噪声)
关于柏林噪声,我曾经写过一篇“基于Perlin Noise实现的Minecraft游戏”,这里也贴一下传送门:
柏林噪声是由Ken Perlin于1983年提出的一种梯度噪声(Gradient Noise,通常由计算机模拟得到的一组噪声,相较于传统的离散数值噪声value noise要更加连续平滑)他在1985年的SIGGRAPH会议上,做了一场以“An Image Synthesizer”为题的学术报告,正式提出他的这一发现。
柏林噪声的应用非常广泛: 合成地形高度图、生成物体表面的复杂纹理、火焰烟雾特效、波动效果的模拟等等。下面的几副图片就是以Perlin Noise为原理做出的效果:
关于Perlin Noise的基本原理,在wikipedia和perlin的个人主页上都能找到,这里不多做赘述,大家可以通过下面的链接找到关于Ken Perlin关于柏林噪声的介绍以及它的其他研究成果(看了一下感觉蛮有意思的):
Ken Perlin的个人主页:http://mrl.nyu.edu/~perlin/(纽约大学-媒体研究实验室 nyu Media Research Lab)
这里只贴一下我的相关实现代码:
class ImageFogger
{
public:
ImageFogger();
QImage processImage(const QImage& img); // 处理img,并返回图像
float m_persistence;
int m_octaveNum;
float m_frequency;
int m_amplitude;
// 噪声相关
double Noise(int x,int y);
double SmoothedNoise(int x, int y);
double Cosine_Interpolate(double a,double b, double x);
double InterpolatedNoise(float x,float y);
double PerlinNoise(float x,float y);
QColor reverseRGB(int r,int g,int b); // 修正像素
};
#include "imagefogger.h"
ImageFogger::ImageFogger()
{
m_persistence = 0.50;
m_octaveNum = 4;
m_frequency = 0.025;
m_amplitude = 200;
}
// 处理图像
QImage ImageFogger::processImage(const QImage &img)
{
QImage pimg = img;
for(int i=0; i<pimg.height() ; i++) // 遍历图像
{
for(int k=0; k<pimg.width(); k++)
{
QRgb pixel = pimg.pixel(k,i);
QColor rgb(pixel);
double noise = m_amplitude*PerlinNoise(k*m_frequency,i*m_frequency); // 获取噪声数值
int r = (noise+rgb.red());
int g = (noise+rgb.green());
int b = (noise+rgb.blue());
pimg.setPixel(k,i,reverseRGB(r,g,b).rgb());
}
}
return pimg;
}
double ImageFogger::Noise(int x,int y) // 根据(x,y)获取一个初步噪声值
{
int n = x + y * 57;
n = (n<<13) ^ n;
return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
}
double ImageFogger::SmoothedNoise(int x, int y) //光滑噪声
{
double corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16;
double sides = ( Noise(x-1, y) +Noise(x+1, y) +Noise(x, y-1) +Noise(x, y+1) ) / 8;
double center = Noise(x, y) / 4;
return corners + sides + center;
}
double ImageFogger::Cosine_Interpolate(double a,double b, double x) // 余弦插值
{
double ft = x * 3.1415927;
double f = (1 - cos(ft)) * 0.5;
return a*(1-f) + b*f;
}
double ImageFogger::InterpolatedNoise(float x,float y) // 获取插值噪声
{
int integer_X = int(x);
float fractional_X = x - integer_X;
int integer_Y = int(y);
float fractional_Y = y - integer_Y;
double v1 = SmoothedNoise(integer_X, integer_Y);
double v2 = SmoothedNoise(integer_X + 1, integer_Y);
double v3 = SmoothedNoise(integer_X, integer_Y + 1);
double v4 = SmoothedNoise(integer_X + 1, integer_Y + 1);
double i1 = Cosine_Interpolate(v1, v2, fractional_X);
double i2 = Cosine_Interpolate(v3, v4, fractional_X);
return Cosine_Interpolate(i1, i2, fractional_Y);
}
double ImageFogger::PerlinNoise(float x,float y) // 最终调用:根据(x,y)获得其对应的PerlinNoise值
{
double total = 0;
double p = m_persistence;
int n = m_octaveNum;
for(int i=0; i<n; i++)
{
double frequency = pow(2,i);
double amplitude = pow(p,i);
total = total + InterpolatedNoise(x * frequency, y * frequency) * amplitude;
}
return total;
}
// 修正像素
QColor ImageFogger::reverseRGB(int r, int g, int b)
{
r = r<(0)?0:r;
g = g<(0)?0:g;
b = b<(0)?0:b;
r = r>(255)?255:r;
g = g>(255)?255:g;
b = b>(255)?255:b;
return QColor(r,g,b);
}
【原理二】 图像刻蚀效果
这个相对来讲就不那么复杂了。 我们可以预先设定一个限制条件t,对于图像中的每个像素,如果它不满足条件t,那么我们就可以剔除该点的像素并以黑色替代。条件t可以随便设置,以实现不同的刻蚀效果,比如:
颜色(r,g,b)的红色分量小于50、红绿分量的壁纸(r/g)小于0.7等。
相关实现的代码如下:
QImage ImageCarver::processImage(const QImage &img)
{
QImage pimg = img;
for(int i=0; i<pimg.height() ; i++) // 遍历y
{
for(int k=0; k<pimg.width(); k++) // 遍历x
{
QRgb pixel = pimg.pixel(k,i);
QColor rgb(pixel); // 获取像素的rgb值
if(rgb.red()/(float)rgb.blue()<0.7)
pimg.setPixel(k,i, QColor(0,0,0) ); // 黑色
else
pimg.setPixel(k,i, rgb); // 保持颜色不变
}
}
return pimg;
}
【原理三】 图像光晕效果
图像的光晕可以通过一种与距离有关的插值实现。实现的步骤如下:
(1) 选定一个点(cx,cy)为光晕的中心。
(2) 遍历图像的每个像素(x,y),计算(x,y)与(cx,cy)的距离dist;
(3) 根据dist修改该像素的颜色值,这里可以随便设置插值函数,比如这里采用平方过渡:
(r',g',b') = (r,g,b) + dist*dist*0.0001;
(4) 修正(r',g',b')的数值在0-255之间,并作为该点的新颜色数值。
三种特效的大概原理就是这样。这个程序是我偶然间想到可以写一个工具来帮助我这种不会用PS的人处理图片,三种特效除了柏林噪声,另两种是我随便琢磨的、觉得可行的方法,没想到效果看起来还挺不错的。欢迎讨论~~