[图像处理] 利用PerlinNoise实现图像雾化效果

来源:互联网 发布:.net cms 源码 编辑:程序博客网 时间:2024/06/05 15:06

时间:2017年7月2日(大三下学期)


写在前面:

        最近觉得Steam的软件界面非常的美观,尤其是图片的渐隐、雾化效果看起来非常漂亮。无奈我PS技术不行,手笨做不出那样的效果…… 于是,我就自己写了一款方便高效的图像处理程序,只需设置相应的参数即可完成处理(懒人模式)。如果点入的读者只是为了使用程序,那么可以无视我下面啰啰嗦嗦的科普,通过下面的链接,就获取到我的打包程序:

                     http://download.csdn.net/detail/mahabharata_/9886497

在正文开始之前,首先贴一下效果图:

(1) 程序界面效果图


(2) 图片的处理效果图:

     

      

     


【原理一】 图像雾化 —— Perlin Noise (柏林噪声)

        关于柏林噪声,我曾经写过一篇“基于Perlin Noise实现的Minecraft游戏”,这里也贴一下传送门:

                  柏林噪声实现的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的人处理图片,三种特效除了柏林噪声,另两种是我随便琢磨的、觉得可行的方法,没想到效果看起来还挺不错的。欢迎讨论~~

阅读全文
1 0