图片预处理技术介绍2——模糊

图片预处理

  大家好,我是阿赵。
  这一篇主要讲一下2种比较常见的模糊算法。

1、 均值模糊

  均值模糊,意思很简单,就是把图片某个像素点周围一定范围内的所有像素点的颜色加起来,然后除以像素的总数量,求出一个平均值。
在这里插入图片描述

  比如半径是1,那么就是会采样9个格子,每个格子取九分之一的色值,再加起来。
比如求中心点的一个均值模糊,如果模糊半径是1,那么就会找中心点一圈的点的色值进行模糊。
在这里插入图片描述

  如果半径是2,那么就会找中心点周围2圈的点的色值进行模糊,那就是取25个格子,然后每个格子的色值取二十五分之一,再加起来。
在这里插入图片描述

  由于是求的平均值,所以中心点的色值并没有什么优势,很容易会被边缘的值平均,而由于范围是一个一个的正方形,所以均值模糊之后,比较容易出现方块感。

public static Texture2D AverageBlur(Texture2D tex,int radius = 1)
{
    if(radius <= 0)
    {
        return tex;
    }
    int width = tex.width;
    int height = tex.height;
    Texture2D newTex = new Texture2D(width, height);
    float[,] colRList = new float[width, height];
    float[,] colGList = new float[width, height];
    float[,] colBList = new float[width, height];
    float[,] colAList = new float[width, height];
    Color[] cols = tex.GetPixels(0, 0, width, height);
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            int index = j * width + i;
            Color targetCol = cols[index];
            colRList[i, j] = targetCol.r;
            colGList[i, j] = targetCol.g;
            colBList[i, j] = targetCol.b;
            colAList[i, j] = targetCol.a;
        }
    }
    float count = 0;
    for(int i = -radius;i<radius+1;i++)
    {
        count++;
    }
    count = count * count;
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            float sumR = 0;
            float sumG = 0;
            float sumB = 0;
            for (int k = -radius; k < radius+1; k++)
            {
                for (int l = -radius; l < radius +1; l++)
                {
                    int x = i + k;
                    int y = j + l;

                    if (x >= 0 && x < width && y >= 0 && y < height)
                    {
                        sumR += colRList[x, y] ;
                        sumG += colGList[x, y] ;
                        sumB += colBList[x, y] ;
                    }
                }
            }
            Color col = new Color(Saturate(sumR/count), Saturate(sumG/count), Saturate(sumB/count), colAList[i,j]);
            newTex.SetPixel(i, j, col);

        }
    }
    newTex.Apply();
    return newTex;
}

2、 高斯模糊

  高斯模糊其实也是取像素点周围的色值取平均,但和均值模糊的区别是,均值模糊每个格子取的比例都一样,但高斯模糊是不一样的,中心的格子取色值的权重会高一些,然后越往边缘,色值的权重越低。所以的色值加起来的权重是1。
  这个权重的框框,我们一般称为卷积核。如果只是简单的实现中间高四周低,那么情况会是这样:
在这里插入图片描述

  但实际上高斯模糊的卷积核,越往外的越低,所以四个角的权重应该比邻边要低,会变成这样:
在这里插入图片描述

  而控制这个卷积核,一般应该有2个参数,第一个是半径,第二个是sigma值。半径控制的是卷积核的大小,sigma值控制的是中间点和边缘点的差异。
  比如同样半径是1,我调整sigma值,可以生成另外一组卷积核:
在这里插入图片描述

  这组卷积核,中间的色值的权重会更高,四角的色值权重会更低,这样做出来的模糊效果四周色值对中间色值的影响会更小。
  再比如,我修改半径,可以得到更大的卷积核:
在这里插入图片描述

  卷积核可以通过公式计算

float wd = (-1 * x * x - y * y) / (2 * spaceSigma * spaceSigma);
wd = Mathf.Exp(wd);

  其中x和y是离中心像素的坐标距离。然后最后通过一个函数exp(x)代表自然指数函数,把计算的取值变成0-1之间,当离中间点像素距离越短,值就越大,离得越远,得到的值就越小。而sigma参数,是调节曲线的弯曲度,值越大,曲线越平缓,那么中心像素和边缘像素的差别就越小。如果sigma参数越小,曲线越陡,那么中心像素和边缘像素的差别就越大。
  如果写一个方法来得到卷积核的数组,大概可以这样:

public static List<float> CreateGaussianFactor(int radius,float spaceSigma = 1)
{
    float count = 0;
    List<float> factorList = new List<float>();
    for (int i = -radius; i < radius + 1; i++)
    {
        for (int j = -radius; j < radius + 1; j++)
        {
            float wd = (-1 * i * i - j * j) / (2 * spaceSigma * spaceSigma);
            wd = Mathf.Exp(wd);
            factorList.Add(wd);
            count += wd;
        }
    }
    for (int i = 0; i < factorList.Count; i++)
    {
        factorList[i] = factorList[i] / count;
    }
    return factorList;
}

在这里插入图片描述

  上图左边是均值模糊,右边是高斯模糊。大概可以看出,高斯模糊的方块感会没那么明显,然后由于中心色值受旁边色值的权重没那么高,所以原像素点的色值会保持得好一些。然后,高斯模糊可以通过sigma值控制中央像素点的色值权重比例,可调性稍微的大一点。

public static Texture2D GaussianBlur(Texture2D tex, int radius = 1,float spaceSigma = 1)
{
    if (radius <= 0)
    {
        return tex;
    }
    int width = tex.width;
    int height = tex.height;
    Texture2D newTex = new Texture2D(width, height);
    float[,] colRList = new float[width, height];
    float[,] colGList = new float[width, height];
    float[,] colBList = new float[width, height];
    float[,] colAList = new float[width, height];
    Color[] cols = tex.GetPixels(0, 0, width, height);
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            int index = j * width + i;
            Color targetCol = cols[index];
            colRList[i, j] = targetCol.r;
            colGList[i, j] = targetCol.g;
            colBList[i, j] = targetCol.b;
            colAList[i, j] = targetCol.a;
        }
    }

    List<float> factorList = CreateGaussianFactor(radius, spaceSigma);
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            float sumR = 0;
            float sumG = 0;
            float sumB = 0;
            int factorIndex = 0;
            for (int k = -radius; k < radius + 1; k++)
            {
                for (int l = -radius; l < radius + 1; l++)
                {
                    int x = i + k;
                    int y = j + l;

                    if (x >= 0 && x < width && y >= 0 && y < height)
                    {
                        float factor = factorList[factorIndex];// radius + 1 - ind;
                        factorIndex++;
                        sumR += colRList[x, y] * factor;
                        sumG += colGList[x, y] * factor;
                        sumB += colBList[x, y] * factor;
                    }
                }
            }
            Color col = new Color(Saturate(sumR), Saturate(sumG), Saturate(sumB), colAList[i,j]);
            newTex.SetPixel(i, j, col);

        }
    }
    newTex.Apply();
    return newTex;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值