OpenCV-C++ 图像滤波(一)-均值滤波-高斯滤波

图像模糊,也可以称为图像滤波,主要是为了去除图像中明显的噪声点;

这一节主要介绍两种滤波方式: 均值滤波和高斯滤波;

重点介绍一下两者的原理,并使用OpenCV提供的API进行测试;

卷积计算

其实,不管是均值滤波,还是高斯滤波,其核心计算是卷积操作;

计算方式如下图所示,通过一个卷积核在图像上以滑动窗口的形式进行计算:

相应位置元素相乘后,累加,再取平均;每一次卷积计算的表达式如下:

\[g(i, j) = \dfrac{1}{k\times l}\sum_{k,l}f(i +k , j+l)h(k, l) \]

其中,\(k ,l\)表示卷积核的尺寸;\(h\)表示卷积核;

因此,卷积计算的核心是卷积核的选择;另外,卷积核的尺寸最好是奇数,因为需要对局部视野中心进行赋值;

均值滤波

均值滤波的卷积核如下所示:

均值滤波的卷积核中所有的元素大小相同,且经过归一化;

OpenCV中均值滤波API blur使用的介绍:

void blur( InputArray src, OutputArray dst,
          Size ksize, Point anchor = Point(-1,-1),
          int borderType = BORDER_DEFAULT );
  • src表示需要被执行均值滤波处理的图像
  • dst表示滤波后的图像
  • ksize表示卷积核的尺寸
  • 其他保持默认即可;

举例, blur的使用(完整代码在文章最后):

Mat dst;
blur(src, dst, Size(3, 3), Point(-1, -1));  // filter2D

高斯滤波

高斯滤波的卷积核不同于均值滤波;

高斯滤波卷积核是通过高斯函数计算出来的,计算方式如下:

\[G(x, y) = \dfrac{1}{2\pi \sigma^2}e^{-\dfrac{x^2+y^2}{2\sigma^2}} \]

将各个位置的坐标带入到高斯函数中,计算得到卷积核,其位置坐标如下所示:

假设,卷积核尺寸选择\(3\times3\),\(\sigma=0.8\),则计算得到的卷积核为:

上面卷积核的计算方式如下:

  • 将位置坐标代码高斯函数中,计算得到每个位置的结果;
  • 将所有位置的值除以所有位置的总和;

这里提供一下python实现的计算方式:

import math
import numpy as np

sigma = 0.8;

def calc_val(x, y, sigma):
    val = (1/(2*math.pi*sigma**2))*(math.e**(-(x**2+y**2)/(2*sigma**2)))
    return val

a_11 = calc_val(-1, 1, sigma)
a_12 = calc_val(0, 1, sigma)
a_13 = calc_val(1, 1, sigma)
a_21 = calc_val(-1, 0, sigma)
a_22 = calc_val(0, 0, sigma)
a_23 = calc_val(1, 0, sigma)
a_31 = calc_val(-1, -1, sigma)
a_32 = calc_val(0, -1, sigma)
a_33 = calc_val(1, -1, sigma)

res = np.array([[a_11, a_12, a_13], 
                [a_21, a_22, a_23], 
                [a_31, a_32, a_33]])
res = res/np.sum(res)
print(res)

那么,由高斯函数计算得到卷积核之后,有两种使用方式:

1. 小数卷积核

小数卷积核指的是直接使用计算得到卷积核用于后续卷积计算;

2. 整数卷积核

整数卷积核指的是将卷积核进行归一化处理;

  • 首先,先将左上角缩放到1,其他位置上的值再乘以这个缩放系数;

得到:

计算方式,python实现如下:

# 归一化, 代码接着上面的来
scale = 1 / a_11
res = res * scale
print(res)
  • 其次, 对计算后的卷积核,取整,再除以和,如下;

从以上的描述过程可以看出,高斯卷积核最重要的参数是高斯分布的标准差\(\sigma\),它代表了数据的离散程度;

  • 如果\(\sigma\)较小,得到的高斯核中心系数就越大,周围系数越小,则对图像的平滑效果不好;
  • 如果\(\sigma\)较大,则高斯核中的各个系数相差不大,则对图像的平滑效果较好;

上面介绍了,如何得到高斯核,那么接下来,看一下OpenCV中提供的GaussianBlur的使用:

void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
                  double sigmaX, double sigmaY = 0,
                  int borderType = BORDER_DEFAULT );
  • src表示需要模糊的图像;
  • dst表示模糊的输出图像;
  • ksize表示卷积核的尺寸;
  • sigmaX表示在X方向的标准偏差;
  • sigmaY表示在Y方向上的标准偏差;

需要注意的是,如果sigmaY=0,则将其设为sigmaX;如果两者都为0,则两者由卷积核的尺寸进行计算:

\[\sigma_x = (\dfrac{n_x-1}{x})\cdot 0.3 + 0.8, \qquad n_x = ksize.width -1 \]

\[\sigma_y = (\dfrac{n_y-1}{x})\cdot 0.3 + 0.8, \qquad n_y = ksize.height -1 \]

注意:这里提到的sigmaX, sigmaY如何与上面叙述的高级核的构造过程如何产生关系,还不得知,以后弄明白了再补充;

示例:

Mat dst_gaussian;
int sigma = 3;
GaussianBlur(src, dst_gaussian, Size(3, 3), sigma, sigma);

最后,完整的程序如下:

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(){

    // 读取图像
    Mat src = imread("/home/chen/dataset/lena.jpg");
    if (src.empty()){
        cout << "cloud not load image." << endl;
        return -1;
    }

    // 均值模糊
    Mat dst;
    blur(src, dst, Size(3, 3), Point(-1, -1));  // filter2D

    // 高斯模糊
    Mat dst_gaussian;
    int sigma = 3;
    GaussianBlur(src, dst_gaussian, Size(3, 3), sigma, sigma);

    // 显示图像
    char input_title[] = "src";
    char output_title[] = "src_blur";
    char output_title_gaussian[] = "src_gaussian_blur";

    namedWindow(input_title, WINDOW_AUTOSIZE);
    imshow(input_title, src);
    namedWindow(output_title, WINDOW_AUTOSIZE);
    imshow(output_title, dst);
    namedWindow(output_title_gaussian, WINDOW_AUTOSIZE);
    imshow(output_title_gaussian, dst_gaussian);

    waitKey(0);
    return 0;
}

输出:

Reference:

posted @ 2021-04-06 22:09  chenzhen0530  阅读(3464)  评论(0编辑  收藏  举报