OpenCV C++(五)----图像平滑

每一幅图像都包含某种程度的噪声,噪声可以理解为由一种或者多种原因造成的灰 度值的随机变化,如由光子通量的随机性造成的噪声等,在大多数情况下,通过平滑技术(也常称为滤波技术)进行抑制或者去除, 其中具备保持边缘(Edge Preserving)作用的平滑技术得到了更多的关注。常用的平滑处理算法包括基于二维离散卷积的高斯平滑、均值平滑,基于统计学方法的中值平滑,具备保持边缘作用的平滑算法的双边滤波、导向滤波等。

5.1、二维离散卷积

I与K的二维离散卷积的计算步骤如下。

  • 第一步: 将K 逆时针翻转180°。

  • 第二步: Kflip沿着I 按照先行后列的顺序移动, 每移动到一个固定位置, 对应位置就相乘, 然后求和。

1、full卷积

显然,高为H1、宽为W1的矩阵I与高为H2、宽为W2的卷积核K 的full卷积结果是一 个高为H1+H2-1、宽为W1+W2-1的矩阵,一般H2 ≤H1,W2 ≤W1。

2、valid卷积

从full卷积的计算过程可知, 如果Kflip靠近I 的边界, 那么就会有部分延伸到I之外而导致访问到未定义的值, 忽略边界,只是考虑I能完全覆盖Kflip内的值的情况, 该过程称为valid卷积。
当然, 只有当H2≤H1且W2≤W1时才会存在 valid卷积 。

image.png

3、same卷积

为了使得到的卷积结果和原图像的高、宽相等,所以通常在计算过程中给Kflip指定 一个“锚点”, 然后将“锚点”循环移至图像矩阵的(r, c) 处, 其中0≤r< H1, 0≤c

image.png

大部分时候,为了更方便地指定卷积核的锚点,通常卷积核的宽、高为奇数,那么可以简单地令中心点为锚点的位置。same卷积是full卷积的一部分,而如果valid卷积存在,那么valid卷积是same卷积的一部分。

4、边界扩充

对于full卷积和same卷积,矩阵I 边界处的值由于缺乏完整的邻接值,因此卷积运算 在这些区域需要特殊处理,方法是进行边界扩充,有如下几种常用方式。

  • (1) 在矩阵I 边界外填充常数, 通常进行的是0扩充。
  • (2) 通过重复I 边界处的行和列, 对输入矩阵进行扩充, 使卷积在边界处可计算。
  • (3) 卷绕输入矩阵, 即矩阵的平铺。
  • (4) 以矩阵边界为中心, 令矩阵外某位置上未定义的灰度值等于图像内其镜像位置 的灰度值, 这种处理方式会令结果产生最小程度的干扰。(最常用

利用上述不同的边界扩充方式得到的same卷积只是在距离矩阵上、下、左、右四个边界小于卷积核半径的区域内值会不同,所以只要在用卷积运算进行图像处理时,图像的重要信息不要落在距离边界小于卷积核半径的区域内就行。

copyMakeBorder(inputArray src,OutputArray dst,int top,int bottom,int left,int right,int borderType,
const Scalar& calue=Scalar())

5、卷积运算

void conv2D(InputArray src, InputArray kernel, OutputArray dst, int ddepth, Point anchor , int borderType)
{
    //step1:卷积核逆时针翻转180°
    Mat kernelFlip;
    flip(kernel, kernelFlip, -1);
    //step:卷积运算
    filter2D(src, dst, ddepth, kernelFlip, anchor, 0.0, borderType);
}
可分离卷积核(速度快

如果一个卷积核至少由两个尺寸比它小的卷积核full卷积而成,并且在计算过程中在所有边界处均进行扩充零的操作,且满足

image.png

其中kerneli的尺寸均比Kernel小,1≤i≤n,则称该卷积核是可分离的。

在图像处理中经常使用这样的卷积核,它可以分离为一维水平方向和一维垂直方向上的卷积核。

(1)full卷积性质

如果卷积核Kernel是可分离的, 且Kernel=kernel1★kernel2, 则有:

image.png

(2)same卷积性质

image.png
void sepConv2D_Y_X(InputArray src, OutputArray src_kerY_kerX, int ddepth, InputArray kernelY, InputArray kernelX, Point anchor , int borderType )
{
    //输入矩阵与垂直方向上的卷积核的卷积
    Mat src_kerY;
    conv2D(src, kernelY, src_kerY, ddepth, anchor, borderType);
    //上面得到的卷积结果,接着和水平方向上的卷积核卷积
    conv2D(src_kerY, kernelX, src_kerY_kerX, ddepth, anchor, borderType);
}

5.2、高斯平滑

  • 第一步: 计算高斯矩阵。
image.png

其中

image.png
  • 第二步: 计算高斯矩阵的和。
image.png
  • 第三步: 高斯矩阵除以其本身的和, 即归一化, 得到的便是高斯卷积算子。
image.png

其中,根据可分离卷积的性质,有

image.png
Mat gaussBlur(const Mat &image, Size winSize, float sigma, int ddepth , Point anchor , int borderType )
{
    //卷积核的宽、高均为奇数
    //CV_ASSERT(winSize.width % 2 == 1 && winSize.height % 2 == 1);
    //构建垂直方向上的高斯卷积算子
    Mat gK_y = getGaussianKernel(sigma, winSize.width, CV_64F);
    //构建水平方向上的高斯卷积算子
    Mat gK_x= getGaussianKernel(sigma, winSize.height, CV_64F);
    gK_x = gK_x.t();//转置
    //分离的高斯卷积
    Mat blurImage;
    sepConv2D_Y_X(image, blurImage, ddepth, gK_y, gK_x, Point(-1, -1), borderType);
    return blurImage;
}

理解了上述高斯平滑的过程, 就可以明白OpenCV实现的高斯平滑函数:

void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
                                double sigmaX, double sigmaY = 0,
                                int borderType = BORDER_DEFAULT );

从参数的设置可以看出, GaussianBlur也是通过分离的高斯卷积核实现的,也可以令水平方向和垂直方向上的标准差不相同,但是一般会取相同的标准差。 当平滑窗口比较小时, 对标准差的变化不是很敏感, 得到的高斯平滑效果差别不大; 相反,当平滑窗口 较大时,对标准差的变化很敏感, 得到的高斯平滑效果差别较大 。

5.3、均值平滑

5.3.1、均值卷积核

image.png

利用卷积核 的分离性和卷积的结合律,虽然减少了运算量,但是随着卷积核窗口的增加,计算量仍会继续增大,可以利用图像的积分,实现时间复杂度为O(1)的快速均值平滑。

5.3.2、快速均值平滑

image.png

即任意一个位置的积分等于该位置左上角所有值的和。 利用矩阵的积分,可以计算出矩阵中任意矩形区域的和。

image.png
    void integral(InputArray src,OutputArray sum,int sdepth=-1)
    void boxFilter(InputArray src,OutputArray dst,int ddepth,Size ksize,Point anchor=Point(-1,-1),
    bool normalize=true,int bordreType=BORDER_DEFAULT)
    void blur(InputArray src,OutputArray dst,Size ksize,Point anchor=Point(-1,-1),int borderType=BORDER_DEFAULT)

5.4、中值平滑

中值滤波最重要的能力是去除椒盐噪声。椒盐噪声是指在图像传输系统中由于解码误差等原因,导致图像中出现孤立的白点或者黑点。

Mat medianSmooth(const Mat& Input, Size size, int borderType )
{
    //输入图像应该为八位的灰度图
    int height = size.height;
    int width = size.width;
    //窗口的高、宽均为奇数,一般设置两者都相同
    //对原图矩阵进行边界扩充
    int h = (height - 1) / 2;
    int w = (width - 1) / 2;
    Mat Ip;
    copyMakeBorder(Input, Ip, h, h, w, w, borderType);
    //输入图像的高、宽
    int rows = Input.rows;
    int cols = Input.cols;
    Mat medianI(Input.size(), CV_8UC1);
    int i = 0, j = 0;
    //中数的位置
    int index = (height*width - 1) / 2;
    for (int r = h; r < h + rows; r++)
    {
        for (int c = w; c < w + cols; c++)
        {
            //取以当前位置为中心、大小为size的邻域
            Mat region = Input(Rect(c - w, r - h, width, height)).clone();
            //将该邻域转换成行矩阵
            cv::sort(region, region, CV_SORT_EVERY_ROW);//注:不能直接使用sort,分不清是cv的
            //取中数
            uchar mValue = region.at(0, index);
            medianI.at(i, j) = mValue;
            j++;
        }
        i++;
        j = 0;
    }
    return medianI;
}

一般来说,如果图像中出现较亮或者较暗的物体,若其大小小于中值平滑的窗口半径,那么它们基本上会被滤掉,而较大的目标则几乎会原封不动地保存下来。

中值平滑需要对邻域中的所有像素点按灰度值排序, 一般比卷积运算要慢。

在OpenCV中同样通过定义函数:

medianBlur(InputArray src,OutputArray dst,int ksize)

此外, 中值平滑只是排序统计平滑中的一种, 如果将取邻域的中值变为取邻域中的 最小值或者最大值, 显然会使图像变暗或者变亮。 这类方法就是后面要介绍的形态学 处理的基础。

高斯平滑、均值平滑在去除图像噪声时,会使图像的边缘信息变得模糊,接下来就 介绍在图像平滑处理过程中可以保持边缘的平滑算法: 双边滤波和导向滤波。

5.5、双边滤波

双边滤波是根据每个位置的邻域, 对该位置构建不同的权重模板。 详细过程如下:

  • 第一步, 构建winH×winW的空间距离权重模板, 与构建高斯卷积核的过程类似, winH 和winW均为奇数。
image.png

其中0≤h

  • 第二步, 构建winH×winW的相似性权重模板, 是通过(r, c) 处的值与其邻域值的差值的指数衡量的。
image.png

其中0≤h

  • 第三步, 将closenessWeightsimilarityWeight的对应位置相乘(即点乘),然后进行归一化,便可得到该位置的权重模板。将所得到的权重模板和该位置邻域的对应位置相乘,然后求和就得到该位置的输出值, 和卷积运算的第二步操作类似。
    void bilateralFilter( InputArray src, OutputArray dst, int d,
                                       double sigmaColor, double sigmaSpace,
                                       int borderType = BORDER_DEFAULT );     

5.6、联合双边滤波

  • 第一步,对每个位置的邻域构建空间距离权重模板。与双边滤波构建空间距离权重模板一样。
  • 第二步,构建相似性权重模板。这是与双边滤波唯一的不同之处,双边滤波是根据原图,对于每一个位置, 通过该位置和其邻域的灰度值的差的指数来估计相似性;而联合双边滤波是首先对原图进行高斯平滑,根据平滑的结果,用当前位置及其邻域的值的差来估计相似性权重模板。
  • 第三步,空间距离权重模板和相似性权重模板点乘,然后归一化,作为最后的权重模板。
  • 第四步将权重模板与原图(注意不是高斯平滑的结果)在该位置的邻域对应位置积 的和作为输出值。

整个过程只在第二步计算相似性权重模板时和双边滤波不同, 但是对图像平滑的效果, 特别是对纹理图像来说, 却有很大的不同。

扩展
循环引导滤波 是一种迭代的方法, 本质上是一种多次迭代的联合双边滤波, 只是每次计算相似性权重 模板的依据不一样——利用本次计算的联合双边滤波结果作为下一次联合双边滤波计算 相似性权重模板的依据。

5.7、导向滤波

导向滤波在平滑图像的基础上,有良好的保边作用, 而且在细节增强等方面都有良好的表现,在执行时间上也比双边滤波快很多。

image.png
image.png

你可能感兴趣的:(OpenCV C++(五)----图像平滑)