图片预处理技术介绍3——锐化

图片预处理

大家好,我是阿赵。

这一篇主要讲一下2种比较基础的图形锐化算法

1、 拉普拉斯锐化

这个算法并不复杂,原理还是逐个像素点找附近的一圈点进行加权运算。

只是它的卷积核会是这样的:

只需要加起来,不需要求平均。也可以是以下这样:

如果当前的像素点和周围的像素点完全一样的情况下,这样算出来,中心点的像素应该是0,因为周围8个和中间的-8刚好抵消。当周围的像素和当前像素不相等,那么就会求出一个差值。

下面以这种被模糊过的图片为例:

经过了拉普拉斯卷积核的运算之后,图形会变成这样:

可以看到线条是有颜色的,如果我们预处理先置灰一下,就会是:

求导这个边缘,我们就可以把这个和原图叠加,让原图的边缘更清晰,达到了锐化的效果。

public static Texture2D LaplaceSharp(Texture2D tex,float origFactor = 1,int radius = 1)
{
    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;
        }
    }
    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;

                    float factor = origFactor*1;
                    if(k == 0&&l == 0)
                    {
                        int r = radius * 2 + 1;
                        factor = -1*origFactor * (r*r-1);
                    }
                    if(x>=0&&x<width&&y>=0&&y<height)
                    {
                        sumR += colRList[x, y] * factor;
                        sumG += colGList[x, y] * factor;
                        sumB += colBList[x, y] * factor;
                    }
                }
            }
            Color col = new Color(colRList[i, j] - Saturate(sumR), colGList[i, j] - Saturate(sumG), colBList[i, j] - Saturate(sumB), colAList[i, j]);
            //Color col = new Color(Saturate(sumR), Saturate(sumG), Saturate(sumB), colAList[i, j]);
            newTex.SetPixel(i, j, col);
            
        }
    }
    newTex.Apply();
    return newTex;
}

由于这个拉普拉斯卷积核只是为了求像素和周围像素色值之间的变化,所以我自己对它进行了一下修改,变成这样:

把周围变成负值,中间变成正值,然后再加多1,意思就是叠加多一次原有颜色,这样就不需要在最后面叠加原图颜色了。通过这个卷积核,得到的效果会是这样:

public static Texture2D LaplaceSharp2(Texture2D tex, float origFactor = 1, int radius = 1)
{
    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;
        }
    }
    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;

                    float factor = origFactor * -1;
                    if (k == 0 && l == 0)
                    {
                        int r = radius * 2 + 1;
                        factor = origFactor * (r * r - 1)+1;
                    }
                    if (x >= 0 && x < width && y >= 0 && y < height)
                    {
                        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;
}

2、 USM锐化

USM 锐化(Unsharp Mask Sharpening)算法的核心很简单

  1. 先求一次高斯模糊
  2. 然后用原图的像素的色值减去高斯模糊后的色值,得到像素差值
  3. 再把像素差值乘以一个锐化因子,得到强化之后的像素差值。
  4. 把强化像素差值和原图像素叠加,得到锐化后的图像。
      求高斯模糊的算法之前已经介绍过了,所以跳过这一步。
      原图形和高斯模糊的结果相减,得到的其实是高斯模糊后被去掉的一些细节的像素。  由于高斯模糊前后的像素颜色其实很接近,所以得到的感觉很微弱,需要乘以一个锐化因子。

当锐化因子不一样时,得到的锐化效果会不一样。

public static Texture2D USMSharp(Texture2D tex,float addValue = 1)
{
    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];
    Color[] cols = tex.GetPixels(0, 0, width, height);
    Texture2D blurTex = GaussianBlur(tex, 2);
    Color[] cols2 = blurTex.GetPixels(0, 0, width, height);
    Color[] cols3 = new Color[cols.Length];
    for(int i = 0;i<cols.Length;i++)
    {
        cols3[i] = cols[i] + (cols[i] - cols2[i]) * addValue;
    }
    newTex.SetPixels(cols3);
    newTex.Apply();
    return newTex;
}