×

图像处理中的滤波

96
芒果浩明
2018.10.06 00:14 字数 1446

滤波

滤波(Wave filtering)是将信号中特定波段频率滤除的操作,是抑制和防止干扰的一项重要措施。在图像处理中,滤波是图像预处理的一种。图像处理中滤波将信号中特定的波段频率滤除,从而保留所需要的波段频率信号。根据选择保留的不同频段可以体现这么两个作用

消除图像中混入的噪声
对应的是低通滤波,噪声在图像中一般是高频信号。
为图像识别抽取出图像特征
这里的特征一般为边缘纹理的特征,对应的是高通滤波,图像中边缘和纹理细节是高频信号。

滤波的分类

图像中滤波算法的分类有很多,可以分为线性滤波和非线性滤波,可以分为相关滤波和卷积滤波,还可以分为高通滤波和低通滤波,空间滤波和频域滤波。

线性滤波和非线性滤波

  • 线性滤波

维基百科解释为:用于时变输入信号的线性运算,在图像处理中可以这么理解,对于输入的信号(即要处理的图像),进行的是线性的运算,得出的结果作为输出图像。可以参考下图


线性滤波原理

首先有一个滤波器的模板(这里是3x3大小),模板里有系数,f(x,y)的值等于模板系数与f(x,y)周围一的像素点相乘求和,这个运算是线性的。在线性的滤波器中,运算的不同即为滤波器的系数模板不同。

线性滤波的包含方框滤波、均值滤波、高斯滤波、拉普拉斯滤波、sobel算子等。

  • 非线性滤波

输出的信号响应是由输入经过非线性的运算得到的。比如典型的中值滤波,就是取像素点邻域的中值作为像素的的响应输出。

非线性滤波包含中值滤波和双边滤波。

卷积滤波和相关滤波

首先要注意的是卷积滤波和相关滤波都属于线性滤波,两者的区别是加权系数的对应相乘顺序有所不同。

卷积和相关

高通滤波和低通滤波

高通滤波与低通滤波之分是相对于滤波的目的而言的,简而言之,高通滤波器就是去除图像中的低频部分,保留高频。表现就是经高通滤波后,保留了图像的高频边缘和纹理细节,所以高通滤波对应的是图像的锐化。低通滤波则是相反的,处理的结果是保留低频部分去除高频部分,在图像上的表现是纹理细节都被模糊了,所以低通滤波对应的是图像的平滑模糊。

空间滤波和频域滤波
空间滤波即直接在像素坐标上对图像数据进行处理滤波,频域滤波则是先把图像由空间域变换到频域,在频域进行处理,结束以后再由频域变换会空间域。

空间滤波和频域滤波.png

下面是几个具体的滤波算法的例子

方框滤波

方框滤波,线性滤波里面最简单的一个。即用一个已确定的模板系数去与像素点领域相乘,所得结果即为像素点的响应。

均值滤波器模板

均值滤波

均值滤波则是方框滤波的特例,将方框滤波的系数模板归一化之后便是均值滤波

均值滤波模板

可以看到,均值滤波的结果是取像素点周围领域的平均值作为响应输出。

高斯滤波

高斯滤波的模板系数就稍显复杂,其模板系数是服从高斯分布的。

先看看一维的高斯分布

一维高斯分布

二维的高斯分布


二维高斯分布

所以高斯滤波的模板系数,应该服从上图中的锥形山峰。那如何计算高斯分布的模板系数呢,我们取均值为模板的中心点,计算公式如下


高斯滤波模板系数计算公式

这里附上代码

std::vector<std::vector<double> >getModel(const int& n, const double& sigmma)//求解nxn的模板系数
{
    const double pi = 3.1415926;
    const double weight = 1.0 /(2.0 * pi * sigmma * sigmma);//gaussion公式中的系数

    double sum = 0.0;
    std::vector<std::vector<double>> res(n, std::vector<double>(n, 0.0));
    
    for(int i = 0;  i < n; ++i)
        for (int j = 0; j < n; ++j)
        {
            res[i][j] = weight * std::exp(-((i - n / 2) * (i - n / 2) + (j - n / 2)*(j - n / 2)) / (2.0*sigmma*sigmma));//n/2为公式中都均值,即原点在图像中心
        }

    return res;
}

假如不想手动计算高斯滤波的模板系数,也可以取模板的近似值作为模板。如3x3和5x5的模板可以取

高斯滤波模板

拉普拉斯算子

拉普拉斯滤波属于高通滤波,算子定义为

拉普拉斯算子定义

下面由算子推导计算模板系数
在x方向上,二阶导数的微分可以由差分近似表示,有

x方向

类似地,在y方向上有

y方向

所以拉普拉斯算子在离散的情况下,可以近似为


离散拉普拉斯

那么可以得矩阵的模板系数

拉普拉斯模板系数

还有一般常用的拉普拉斯模板

拉普拉斯模板

Roberts算子

Roberts算子比较简单,使用2x2的领域


Roberts算子

计算响应值的方式如下

Roberts算子计算

sobel算子

sobel算子

Robinson算子

Robinson算子

Kirsch算子

Kirsch算子

以上所有有的算子都属于线性滤波,在得到离散情况下的算子模板系数之后,就可以用这个模板去对图像进行操作,使用模板系数去遍历图像,操作可以选择卷积或者相关。这些算子也称卷积掩模。

这里先给出二维卷积的代码实现

//二维卷积的实现
#include<cassert>
#include<vector>


void conv2(int** filter, int **mat, int** res, const int filter_rows, const int filter_cols, const int mat_rows, const int mat_cols);//指针数组版本
std::vector<std::vector<int> > conv2(std::vector<std::vector<int> > filter, std::vector<std::vector<int> > mat);//向量版本


int main(void)
{
    return 0;
}//main

void conv2(int** filter, int **mat, int** res, const int filter_rows, const int filter_cols, const int mat_rows, const int mat_cols)
{
    assert(filter_cols < mat_cols && filter_rows < mat_rows);
    for(int i = 0; i < mat_rows - 1; ++i)
        for (int j = 0; j < mat_cols - 1; ++j)
        {
            int tmp = 0;
            for (int m = 0; m < filter_rows; ++m)
                for (int n = 0; n < filter_cols; ++n)
                    if(0 <= i -m  && i - m < mat_rows && 0 <= j - n && j - n < mat_cols)
                        tmp += filter[m][n] * mat[i - m][j - n];//卷积公式

            res[i][j] = tmp;
        }
}

std::vector<std::vector<int> > conv2(std::vector<std::vector<int> > filter, std::vector<std::vector<int> > mat )//向量版本
{
    const int filter_rows = filter.size();
    const int filter_cols = filter[0].size();

    const int mat_rows = mat.size();
    const int mat_cols = mat[0].size();

    assert(filter_cols < mat_cols && filter_rows < mat_rows);
    std::vector<std::vector<int> > res(mat_rows, std::vector<int>(mat_cols, 0));

    for (int i = 0; i < mat_rows - 1; ++i)
        for (int j = 0; j < mat_cols - 1; ++j)
        {
            int tmp = 0;
            for (int m = 0; m < filter_rows; ++m)
                for (int n = 0; n < filter_cols; ++n)
                    if (0 <= i - m && i - m < mat_rows && 0 <= j - n && j - n < mat_cols)
                        tmp += filter[m][n] * mat[i - m][j - n];//卷积公式

            res[i][j] = tmp;
        }
    return res;
}

然后以高斯滤波为例,给出这些模板系数卷积滤波的实现

void mycv::gaussianFilter(cv::Mat& src, cv::Mat& dst)
{
    const int rows = src.rows;
    const int cols = src.cols;
    dst = cv::Mat(src.size(), src.type(), cv::Scalar::all(0));

    std::vector<std::vector<double>> gauss = getModel(3);//3x3模板系数

    switch (src.channels())
    {
    case 1://灰度图
        for(int i = 0; i < rows -1; ++i)
            for (int j = 0; j < cols - 1; ++j)
            {
                double tmp = 0.0;
                for(int m = 0; m < gauss.size(); ++m)
                    for (int n = 0; n < gauss[0].size(); ++n)
                    {
                        if (i - m >= 0 & i - m < rows && j - n >= 0 && j - n < cols)
                            tmp += gauss[m][n] * static_cast<double>(src.at<uchar>(i - m, j - n));//卷积公式
                    }

                dst.at<uchar>(i, j) = static_cast<int>(tmp);
            }
        break;
    case 3://彩色
        for (int i = 0; i < rows - 1; ++i)
            for (int j = 0; j < cols - 1; ++j)
                for (int k = 0; k < 3; ++k)
                {
                    double tmp = 0.0;
                    for (int m = 0; m < gauss.size(); ++m)
                        for (int n = 0; n < gauss[0].size(); ++n)
                            if (i - m >= 0 & i - m < rows && j - n >= 0 && j - n < cols)
                                tmp += gauss[m][n] * static_cast<double>(src.at<cv::Vec3b>(i - m, j - n)[k]);//卷积公式

                    dst.at<cv::Vec3b>(i, j)[k] = static_cast<int>(tmp);
                }
        break;
    default:
        break;
    }
}
计算机视觉
Web note ad 1