[图像处理] 利用PerlinNoise实现图像雾化效果 - 附我的图像处理程序(三种特效)

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

猜你喜欢

转载自blog.csdn.net/Mahabharata_/article/details/74092399