在进行图像分割之前通常会使用滤波器进行平滑操作,其目的是消除高斯噪声的影响。学习高斯噪声/高斯滤波的相关概念并实现高斯滤波器
高斯分布
参考:
一维高斯分布
二维高斯分布
每次计算均以当前像素点为中心,所以均值$\mu$为$0$
标准差$\sigma$控制离散程度,$\sigma$越大,曲线越扁平,数据分布越离散,滤波效果越明显;$\sigma$越小,曲线越廋高,数据分布越集中,滤波效果不明显
1 | # -*- coding: utf-8 -*- |
高斯噪声
参考:高斯噪声
高斯噪声是指它的概率密度函数服从高斯分布(即正态分布)的一类噪声
数字图像中的高斯噪声的主要来源出现在采集期间,由于不良照明/高温引起的传感器噪声
高斯滤波
高斯滤波(gaussian filter
)是一种线性平滑滤波,就是对整幅图像进行加权平均的过程,对每个像素点的值,结合邻域内其他像素值进行加权平均
具体操作如下:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域像素的加权平均灰度值去替代模板中心像素点的值
高斯滤波的优点在于消除高斯噪声,其副作用是消除图像细节,所以又称为高斯模糊(gaussian blur
)
模板
参考:关于高斯滤波的一些理解
有两个常用的高斯模板,分别是$3\times 3$和$5\times 5$大小
滤波过程中的模板是通过高斯公式计算得到的,以$3\times 3$大小模板为例,其原始值是
1 | [[2 1 2] |
假定$\sigma=0.85$,输入到二维高斯函数计算得到
1 | [[0.11759572 0.23493154 0.11759572] |
进行归一化
1 | [[0.06256912 0.12499996 0.06256912] |
除以最小值
1 | [[1 2 1] |
$5\times 5$大小模板同理,不过其$\sigma$约为$1.04$
1 | if __name__ == '__main__': |
彩色图像
高斯滤波默认对单个通道图像进行,所以对于彩色图像,需要先分离为3
个单通道图像,分别进行滤波处理后再合并为3
通道图像
opencv实现
参考:
opencv
提供了高斯滤波以及高斯模板的实现
源码位于:path/to/modules/imgproc/src/smooth.cpp
getGaussianKernel
函数getGaussianKernel()计算并返回高斯滤波系数的$ksize×1$大小矩阵:
- 参数$ksize$应该是正奇数(
1/3/5/...
),如果输入为0
(即Size(0,0)
),则根据sigma
值进行计算 - 参数$\sigma$是高斯标准差,如果输入不为正,根据
ksize
计算$sigma = 0.3*((ksize-1)\cdot 0.5 - 1)+0.8$,当ksize=3, sigma=0.8
,当ksize=5, sigma=1.1
- 参数$i$遍历$0,…,ksize-1$
- 参数$\alpha$是缩放因子,其目的是使矩阵归一化:$\sum_{i}G_{i} = 1$
getGaussianKernel
源码位于/path/to/modules/imgproc/src/smooth.cpp
当ksize=Size(0,0)
时,计算如下:
1 | // automatic detection of kernel size from sigma |
- 当
sigma=1.0
时,ksize.width = cvRound(7)|1 = 0111|1 = 0111 = 7
- 当
sigma=0.8
时,ksize.width = cvRound(5.8)|1 = 6|1 = 0110|1 = 0111 = 7
- 当
sigma=0.5
时,ksize.width = cvRound(4)|1 = 0100|1 = 0101 = 5
GaussianBlur
函数GaussianBlur()将源图像与高斯核进行卷积操作
使用$(5,5)$大小,$sigma=1$的高斯核进行卷积操作
python语言
1 | import cv2 |
C++语言
1 | #include <iostream> |
自定义实现(c++)
创建类GaussianFilter
,实现获取高斯滤波功能
- 使用
gaussFilter2d
实现高斯分布计算(去掉weight
计算,其在归一化过程中不需要) - 使用
Conv2d
实现卷积核与图像计算 - 使用
getGaussianKernel
计算高斯模板 - 使用
GaussianBlur
进行高斯滤波
1 | // |
1 | // |
优化
参考:
二维高斯分布公式可由两个一维高斯分布公式组成:
利用二维高斯模板对图像进行滤波:
其中$I()$表示图像像素值,$r$表示模板半径,模板长宽为$2\cdot r+1$
利用一维高斯分布公式进行优化如下:
可以先在垂直方向对图像进行一维高斯变换,再在水平方向对图像进行一维高斯变换
按照原先的二维模板计算方式,对单个图像像素进行计算需要$(2\cdot r+1)^{2}$次乘法以及$(2\cdot r+1)^{2}-1$次加法,时间复杂度为$O(n^{2})$
如果转换成一维模板计算,先进行垂直方向计算,需要$(2\cdot r+1)$次乘法,$(2\cdot r+1)-1$次加法。对于水平方向计算类似,总共需要$2\cdot(2\cdot r+1)$次乘法,$2\cdot(2\cdot r+1)-2$次加法,时间复杂度为$O(n)$
单单对单个图像像素进行计算不太好理解,好像使用一维模板计算的只是沿该像素点水平和垂直的领域像素,没有涉及到对角领域像素;如果扩大到整副图像就好理解了
修改后的GaussianFilter
实现如下:
getGaussianKernel
生成一维高斯算子,参考OpenCV
实现使用固定模板GaussianBlur
先对水平方向进行高斯滤波,再对垂直方向进行高斯滤波
完整类定义如下:
1 | // |
1 | // |
测试代码如下:
1 | #include <sys/time.h> |
CMakeLists.txt
内容如下:
1 | cmake_minimum_required(VERSION 3.13) |
测试结果如下(单位:ms
):
OpenCV | 自定义 | 优化 | |
---|---|---|---|
3x3 | 4 | 67 | 64 |
5x5 | 6 | 182 | 94 |
进一步优化方向是多线程、编译优化、模板查表等