新普金娱乐网址


钱钟书同杨绛—《我受见她之前,从未想到如果完婚;我娶了它们随后,也不想过如果娶别的内。》

于“冬眠”到“冬泳”——小明微课“如何保障青春的心怀”听后谢

风土高斯模糊和优化算法(附完整C++代码)

  • 十月 15, 2018
  • 数学
  • 没有评论

高斯模糊(英语:Gaussian Blur),也深受高斯平滑,是当Adobe
Photoshop、GIMP以及Paint.NET等图像处理软件中广大应用的处理效果,通常用它来减图像噪声和降低细节层次。这种歪曲技术转移的图像,其视觉效果就如是由此一个半晶莹剔透屏幕在考察图像,这和画面焦外成像效果散景以及日常照明阴影中之功用还显然不同。高斯平滑也用于计算机视觉算法中之事先处理等,以增长图像在不同比例大小下之图像效果(参见尺度空间表示和尺度空间实现)。
从数学之角度来拘禁,图像的高斯模糊过程就是图像和正态分布做卷积。由于正态分布又于作高斯分布,所以这项技能就是深受作高斯歪曲。图像以及环方框模糊做卷积将见面变更加纯粹的焦外成像效果。由于高斯函数的傅立叶变换是另外一个高斯函数,所以高斯模糊对于图像来说即使是一个低通滤波器。

运算符是用来检查,更改或者组合值的特殊符号或短语。Swift提供的森例行的运算符,如+、-、*、/、%、=、==等,以及逻辑运算的&&、||等等,基本上不需重新介绍,我们于这边仅需要了解一些免太一致的运算符就得了。如Swift引入的新运算符,范围操作符号,包括..<和…两只,该随笔介绍Swift常规的运算符中,以及跟外语言有区别的一些。

 

 

高斯模糊是均等种植图像模糊滤波器,它用正态分布测算图像中每个像素的变换。N维空间正态分布方程为

赋值运算符

let b = 10
var a = 5
a = b
// a is now equal to 10

 赋值语句,处理与另外语言一样。

 

let (x, y) = (1, 2)
// x is equal to 1, and y is equal to 2

 这种代码是类似ECMAScript
6的剧本写法,通过将右侧元祖对象解构赋值给左对应的参数。

 

图片 1

数学运算符

1 + 2       // equals 3
5 - 3       // equals 2
2 * 3       // equals 6
10.0 / 2.5  // equals 4.0

 这些都是暨另外语言没有呀两样,循例列出参考下

于字符,也堪下+符号进行连续新的字符串

"hello, " + "world"  // equals "hello, world"

 

一元操作符中之-、+运算,和算术里面的负负得正,正负得负的意思同样了。

let three = 3
let minusThree = -three       // minusThree equals -3
let plusThree = -minusThree   // plusThree equals 3, or "minus minus three"

 

let minusSix = -6
let alsoMinusSix = +minusSix  // alsoMinusSix equals -6

 

组合运算符提供+= 、-=的运算符操作

var a = 1
a += 2
// a is now equal to 3

 

比运算符和另语言差不多

  • 等于 (a == b)

  • 不等于 (a != b)

  • 大于 (a > b)

  • 小于 (a < b)

  • 过等于 (a >= b)

  • 低于等于 (a <= b)

除此以外值得注意的凡,Swift提供了比引用的少只操作符号,=== 和 !==,用来检查两个引用是否完全相等;或者不相等的。而==只是用来对比两个对象的值是否一致。

1 == 1   // true because 1 is equal to 1
2 != 1   // true because 2 is not equal to 1
2 > 1    // true because 2 is greater than 1
1 < 2    // true because 1 is less than 2
1 >= 1   // true because 1 is greater than or equal to 1
2 <= 1   // false because 2 is not less than or equal to 1

 对比运算符也常常用来If条件语句里面

let name = "world"
if name == "world" {
    print("hello, world")
} else {
    print("I'm sorry \(name), but I don't recognize you")
}
// Prints "hello, world", because name is indeed equal to "world".

 

在二维空间定义为

元旦运算符

元旦运算符 ? :和C#其间表现是同的

question ? answer1 : answer2

 

let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)

 

图片 2

空值转换操作符

空值转换符是对而空类型(可选类型)的一个值得转换出来(a ?? b)。

let defaultColorName = "red"
var userDefinedColorName: String?   // defaults to nil

var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName is nil, so colorNameToUse is set to the default of "red"

 

userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName is not nil, so colorNameToUse is set to "green"

 

其中r凡张冠李戴半径
图片 3),σ是正态分布之规范不是。在二维空间受到,这个公式生成的曲面的等高线凡由着力开始上正态分布的同心圆。分布不为零星之像素组成的卷积矩阵与旧图像做变换。每个像素的价值都是周围相邻像素值的加权平均。原始像素的价有尽老的高斯分布值,所以发生太深之权重,相邻像素随着距离原始像素越来越远,其权重为进一步小。这样进行模糊处理比较任何的均匀模糊滤波器更胜地保留了边缘效果,参见尺度空间实现。

范围操作符

闭范围运算符 … 和半闭合范围运算符 ..< 两单

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25

 半闭合的界定运算符

let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
    print("Person \(i + 1) is called \(names[i])")
}
// Person 1 is called Anna
// Person 2 is called Alex
// Person 3 is called Brian
// Person 4 is called Jack

 或者正如使用

for name in names[..<2] {
    print(name)
}
// Anna
// Alex

 

同一旁限量之运算符,包括左侧和右两单有

for name in names[2...] {
    print(name)
}
// Brian
// Jack

for name in names[...2] {
    print(name)
}
// Anna
// Alex
// Brian

 

let range = ...5
range.contains(7)   // false
range.contains(4)   // true
range.contains(-1)  // true

 

答辩及来讲,图像遭到每点的遍布且不呢零星,这吗便是说每个像素的计都待包含整幅图像。在实际上应用中,在测算高斯函数的偏离散近似时,在大概3σ距离外的像素都得以当作不起作用,这些像从的乘除也就算可忽略。通常,图像处理程序只待算图片 4的矩阵就得保相关像从影响。对于边界上之接触,通常采取复制周围的触及及其他一样直面重新拓展加权平均运算。

逻辑运算符

let allowedEntry = false
if !allowedEntry {
    print("ACCESS DENIED")
}
// Prints "ACCESS DENIED"

 

let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "ACCESS DENIED"

 

let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

 

if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

要么应用括号使的进一步便利阅读

if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

 

而外圆形对称之外,高斯模糊也得以以二维图像上针对少数单独立的均等维空间分别进行测算,这吃作线性可划分。这为即,使用二维矩阵变换得到的作用也足以经在档次方向拓展一维高斯矩阵变换加上竖直方向的一致维高斯矩阵变换得到。从计算的角度来拘禁,这是平等件中之特性,因为这么单需要图片 5不行计算,而不可分的矩阵则需图片 6次计算,其中图片 7,图片 8凡是要开展滤波的图像的维数,图片 9图片 10凡滤波器的维数。

本着相同幅图像进行多次连连高斯模糊的机能与平等坏更甚之高斯模糊可以来相同的功用,大的高斯模糊的半径是所用几近单高斯模糊半径平方和的平方根。例如,使用半径分别吗6与8的有限糟高斯模糊变得到的功效等同于平赖半径为10的高斯模糊效果,图片 11。根据此涉及,使用多独连较小之高斯模糊处理不见面比单个高斯较充分拍卖时要掉。

当抽图像尺寸的场地常使用高斯歪曲。在拓展欠采样的当儿,通常以采样之前对图像进行小通滤波处理。这样即使足以确保在采样图像中无会见现出假的多次信息。高斯模糊有特别好之性状,如没确定性的境界,这样尽管不见面于滤波图像遭到形成震荡。

以上资料摘自维基百科(高斯模糊词条):

https://zh.wikipedia.org/wiki/%E9%AB%98%E6%96%AF%E6%A8%A1%E7%B3%8A

那具体什么促成吗?

代码献上:

inline int* buildGaussKern(int winSize, int sigma)
{
    int wincenter, x;
    float   sum = 0.0f;
    wincenter = winSize / 2;
    float *kern = (float*)malloc(winSize*sizeof(float));
    int *ikern = (int*)malloc(winSize*sizeof(int));
    float SQRT_2PI = 2.506628274631f;
    float sigmaMul2PI = 1.0f / (sigma * SQRT_2PI);
    float divSigmaPow2 = 1.0f / (2.0f * sigma * sigma);
    for (x = 0; x < wincenter + 1; x++)
    {
        kern[wincenter - x] = kern[wincenter + x] = exp(-(x * x)* divSigmaPow2) * sigmaMul2PI;
        sum += kern[wincenter - x] + ((x != 0) ? kern[wincenter + x] : 0.0);
    }
    sum = 1.0f / sum;
    for (x = 0; x < winSize; x++)
    {
        kern[x] *= sum;
        ikern[x] = kern[x] * 256.0f;
    }
    free(kern);
    return ikern;
}

void GaussBlur(unsigned char*  pixels, unsigned int    width, unsigned int  height, unsigned  int channels, int sigma)
{
    width = 3 * width;
    if ((width % 4) != 0) width += (4 - (width % 4));

    unsigned int  winsize = (1 + (((int)ceil(3 * sigma)) * 2));
    int *gaussKern = buildGaussKern(winsize, sigma);
    winsize *= 3;
    unsigned int  halfsize = winsize / 2;

    unsigned char *tmpBuffer = (unsigned char*)malloc(width * height* sizeof(unsigned char));

    for (unsigned int h = 0; h < height; h++)
    {
        unsigned int  rowWidth = h * width;

        for (unsigned int w = 0; w < width; w += channels)
        {
            unsigned int rowR = 0;
            unsigned int rowG = 0;
            unsigned int rowB = 0;
            int * gaussKernPtr = gaussKern;
            int whalfsize = w + width - halfsize;
            unsigned int  curPos = rowWidth + w;
            for (unsigned int k = 1; k < winsize; k += channels)
            {
                unsigned int  pos = rowWidth + ((k + whalfsize) % width);
                int fkern = *gaussKernPtr++;
                rowR += (pixels[pos] * fkern);
                rowG += (pixels[pos + 1] * fkern);
                rowB += (pixels[pos + 2] * fkern);
            }

            tmpBuffer[curPos] = ((unsigned char)(rowR >> 8));
            tmpBuffer[curPos + 1] = ((unsigned char)(rowG >> 8));
            tmpBuffer[curPos + 2] = ((unsigned char)(rowB >> 8));

        }
    }
    winsize /= 3;
    halfsize = winsize / 2;
    for (unsigned int w = 0; w < width; w++)
    {
        for (unsigned int h = 0; h < height; h++)
        {
            unsigned    int col_all = 0;
            int hhalfsize = h + height - halfsize;
            for (unsigned int k = 0; k < winsize; k++)
            {
                col_all += tmpBuffer[((k + hhalfsize) % height)* width + w] * gaussKern[k];
            }
            pixels[h * width + w] = (unsigned char)(col_all >> 8);
        }
    }
    free(tmpBuffer);
    free(gaussKern); 
}

备注:

的为原始算法,我开了部分略改变,主要是为着考虑一点点性能达到的题材。

偶见面写最好多注释反而显得啰嗦,所以将就正在看哈。

马上卖代码,实测速度非常糟糕,处理同摆放5000×3000在半径大小5错右都使耗时十来秒至几十秒不顶,实在难以承受。

出于速度的问题,网上虽时有发生众多优化算法的实现。

事先我呢发过一篇《敏捷高斯模糊算法》,在同等条件下,这个算法就随经济法快上十几加倍。

鉴于当下卖代码实在麻烦阅读学习,所以,我本着该展开了更进一步的调整及优化。

void GaussianBlur(unsigned char* img,  unsigned int width, unsigned int height, unsigned int channels, unsigned int radius)
{
    radius = min(max(1, radius), 248);
    unsigned int kernelSize = 1 + radius * 2;
    unsigned int* kernel = (unsigned int*)malloc(kernelSize* sizeof(unsigned int));
    memset(kernel, 0, kernelSize* sizeof(unsigned int));
    int(*mult)[256] = (int(*)[256])malloc(kernelSize * 256 * sizeof(int));
    memset(mult, 0, kernelSize * 256 * sizeof(int));

    int xStart = 0;
    int yStart = 0;
    width = xStart + width - max(0, (xStart + width) - width);
    height = yStart + height - max(0, (yStart + height) - height);
    int imageSize = width*height;
    int widthstep = width*channels;
    if (channels == 3 || channels == 4)
    {
        unsigned char *    CacheImg = nullptr;
        CacheImg = (unsigned char *)malloc(sizeof(unsigned char) * imageSize * 6);
        if (CacheImg == nullptr) return;
        unsigned char *    rCache = CacheImg;
        unsigned char *    gCache = CacheImg + imageSize;
        unsigned char *    bCache = CacheImg + imageSize * 2;
        unsigned char *    r2Cache = CacheImg + imageSize * 3;
        unsigned char *    g2Cache = CacheImg + imageSize * 4;
        unsigned char *    b2Cache = CacheImg + imageSize * 5;
        int sum = 0;
        for (int K = 1; K < radius; K++){
            unsigned int szi = radius - K;
            kernel[radius + K] = kernel[szi] = szi*szi;
            sum += kernel[szi] + kernel[szi];
            for (int j = 0; j < 256; j++){
                mult[radius + K][j] = mult[szi][j] = kernel[szi] * j;
            }
        }
        kernel[radius] = radius*radius;
        sum += kernel[radius];
        for (int j = 0; j < 256; j++){
            mult[radius][j] = kernel[radius] * j;
        }
        for (int Y = 0; Y < height; ++Y) {
            unsigned char*     LinePS = img + Y*widthstep;
            unsigned char*     LinePR = rCache + Y*width;
            unsigned char*     LinePG = gCache + Y*width;
            unsigned char*     LinePB = bCache + Y*width;
            for (int X = 0; X < width; ++X) {
                int     p2 = X*channels;
                LinePR[X] = LinePS[p2];
                LinePG[X] = LinePS[p2 + 1];
                LinePB[X] = LinePS[p2 + 2];
            }
        }
        int kernelsum = 0;
        for (int K = 0; K < kernelSize; K++){
            kernelsum += kernel[K];
        }
        float fkernelsum = 1.0f / kernelsum;
        for (int Y = yStart; Y < height; Y++){
            int heightStep = Y * width;
            unsigned char*     LinePR = rCache + heightStep;
            unsigned char*     LinePG = gCache + heightStep;
            unsigned char*     LinePB = bCache + heightStep;
            for (int X = xStart; X < width; X++){
                int cb = 0;
                int cg = 0;
                int cr = 0;
                for (int K = 0; K < kernelSize; K++){
                    unsigned    int     readPos = ((X - radius + K + width) % width);
                    int * pmult = mult[K];
                    cr += pmult[LinePR[readPos]];
                    cg += pmult[LinePG[readPos]];
                    cb += pmult[LinePB[readPos]];
                }
                unsigned int p = heightStep + X;
                r2Cache[p] = cr* fkernelsum;
                g2Cache[p] = cg* fkernelsum;
                b2Cache[p] = cb* fkernelsum;
            }
        }
        for (int X = xStart; X < width; X++){
            int WidthComp = X*channels;
            int WidthStep = width*channels;
            unsigned char*     LinePS = img + X*channels;
            unsigned char*     LinePR = r2Cache + X;
            unsigned char*     LinePG = g2Cache + X;
            unsigned char*     LinePB = b2Cache + X;
            for (int Y = yStart; Y < height; Y++){
                int cb = 0;
                int cg = 0;
                int cr = 0;
                for (int K = 0; K < kernelSize; K++){
                    unsigned int   readPos = ((Y - radius + K + height) % height) * width;
                    int * pmult = mult[K];
                    cr += pmult[LinePR[readPos]];
                    cg += pmult[LinePG[readPos]];
                    cb += pmult[LinePB[readPos]];
                }
                int    p = Y*WidthStep;
                LinePS[p] = (unsigned char)(cr * fkernelsum);
                LinePS[p + 1] = (unsigned char)(cg * fkernelsum);
                LinePS[p + 2] = (unsigned char)(cb* fkernelsum);


            }
        }
        free(CacheImg);
    }
    else if (channels == 1)
    {
        unsigned char *    CacheImg = nullptr;
        CacheImg = (unsigned char *)malloc(sizeof(unsigned char) * imageSize * 2);
        if (CacheImg == nullptr) return;
        unsigned char *    rCache = CacheImg;
        unsigned char *    r2Cache = CacheImg + imageSize;

        int sum = 0;
        for (int K = 1; K < radius; K++){
            unsigned int szi = radius - K;
            kernel[radius + K] = kernel[szi] = szi*szi;
            sum += kernel[szi] + kernel[szi];
            for (int j = 0; j < 256; j++){
                mult[radius + K][j] = mult[szi][j] = kernel[szi] * j;
            }
        }
        kernel[radius] = radius*radius;
        sum += kernel[radius];
        for (int j = 0; j < 256; j++){
            mult[radius][j] = kernel[radius] * j;
        }
        for (int Y = 0; Y < height; ++Y) {
            unsigned char*     LinePS = img + Y*widthstep;
            unsigned char*     LinePR = rCache + Y*width;
            for (int X = 0; X < width; ++X) {
                LinePR[X] = LinePS[X];
            }
        }
        int kernelsum = 0;
        for (int K = 0; K < kernelSize; K++){
            kernelsum += kernel[K];
        }
        float fkernelsum = 1.0f / kernelsum;
        for (int Y = yStart; Y < height; Y++){
            int heightStep = Y * width;
            unsigned char*     LinePR = rCache + heightStep;
            for (int X = xStart; X < width; X++){
                int cb = 0;
                int cg = 0;
                int cr = 0;
                for (int K = 0; K < kernelSize; K++){
                    unsigned    int     readPos = ( (X - radius + K+width)%width);
                    int * pmult = mult[K];
                    cr += pmult[LinePR[readPos]];
                }
                unsigned int p = heightStep + X;
                r2Cache[p] = cr * fkernelsum;
            }
        }
        for (int X = xStart; X < width; X++){
            int WidthComp = X*channels;
            int WidthStep = width*channels;
            unsigned char*     LinePS = img + X*channels;
            unsigned char*     LinePR = r2Cache + X;
            for (int Y = yStart; Y < height; Y++){
                int cb = 0;
                int cg = 0;
                int cr = 0;
                for (int K = 0; K < kernelSize; K++){
                    unsigned int   readPos = ((Y - radius + K+height)%height) * width;
                    int * pmult = mult[K];
                    cr += pmult[LinePR[readPos]];
                }
                int    p = Y*WidthStep;
                LinePS[p] = (unsigned char)(cr* fkernelsum);
            }
        }
        free(CacheImg);
    } 
    free(kernel);
    free(mult);
}

  其中起一对算法优化技术,想来也克于及某些抛砖引玉的来意。

贴单职能图:

图片 12

本文只是抛砖引玉一下,若发生另外相关问题或者要求吗堪邮件联系自身探讨。

 邮箱地址是:

gaozhihan@vip.qq.com

相关文章

No Comments, Be The First!
近期评论
    分类目录
    功能
    网站地图xml地图