简述:
图像滤波是为了去除图像中存在的噪声,提升图像的有效信息清晰度。一般情况下,噪声在傅里叶变换频谱中处于图像中的高频段,所以衍生出一系列低通滤波算法,(Lower Pass Filter, LPF)算法在滤除噪声的同时也会一定程度的对边缘高频信息削弱。通常,滤波算法主要通过与图形进行卷积运算来进行图像滤波。早期滤波算法主要有:均值滤波、中值滤波、高斯滤波、双边滤波算法等。在介绍各个滤波算法之前,我们先简单介绍一下卷积运算操作:
卷积运算:卷积核函数在图像上从左往右、从上往下进行运算,对应位置相乘然后进行求和就是卷积运算的结果。
介绍完卷积运算之后,下面叙述的各个滤波算法都是设计不同的卷积核参数来进行不同功能的图像滤波算法。但是,最终的目的都是一样:最大程度的滤除噪声和最大程度保留图像原始的信息。
均值滤波:
均值滤波:简单来说就是统计一定邻域像素的和求平均值来降低噪声的干扰。OpenCV-Python中其滤波函数为cv2.filter2D()、cv2.blur()、cv2.boxFilter()三个函数都是均值滤波可以使用。下面通过函数接口声明分析一下接口的参数并且进行分析这三个均值滤波函数的异同点。
def filter2D(src, ddepth, kernel, dst=None, anchor=None, delta=None, borderType=None)
参数简要说明:
src: 输入图像矩阵;
ddepth: 图像的深度,默认与输入图像一致;
kernel: 卷积核函数,需要事先声明卷积核函数;例如5x5卷积核:
kernel = np.ones( (5, 5), np.float) / 25
dst: 输出图像;
anchor: 卷积内核的锚点,默认参数(-1, -1)为内核中心;
delta: 将delta阈值添加到dst输出矩阵中;
borderType: 卷积到图像边界如何填充像素方法;
def blur(src, ksize, dst=None, anchor=None, borderType=None):
参数简要说明(重复参数参考filter2D):
ksize: 卷积核大小,无需filter2D设置kernel的全部参数,只需设置卷积核大小;
blur()滤波函数内部调用boxFilter()函数进行卷积滤波;源码部分如下:
void cv::blur( InputArray src, OutputArray dst, Size ksize, Point anchor, int borderType )
{
CV_INSTRUMENT_REGION();
boxFilter( src, dst, -1, ksize, anchor, true, borderType );
}
def boxFilter(src, ddepth, ksize, dst=None, anchor=None, normalize=None, borderType=None)
参数简要说明(重复参数参考filter2D):
ksize: 参数含义同blur;
normalize: 是否在核函数按照指定区域进行归一化;
简单调用OpenCV-Python滤波函数接口及其实现结果:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
image_path = './data/filter_input.jpg'
img = cv.imread(image_path)
kernel = np.ones((5, 5), np.float32)/25
dst = cv.filter2D(img, -1, kernel)
# kernel_size = (3, 3)
# dst_box = cv.boxFilter(img, -1, kernel_size, normalize=True)
# dst = cv.blur(img, kernel_size)
show_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
plt.subplot(121), plt.imshow(show_img), plt.title('Original')
plt.xticks([]), plt.yticks([])
show_dst = cv.cvtColor(dst, cv.COLOR_BGR2RGB)
plt.subplot(122), plt.imshow(show_dst), plt.title('Averaging')
plt.xticks([]), plt.yticks([])
plt.show()
中值滤波
图像中噪声的类别也有多种,其中一种就是椒盐噪声(salt and pepper noise)。针对这种特殊的噪声,研究者们发明一种专门滤除椒盐噪声的中值滤波算法。其思想现在看来比较简单:将窗口中的像素值进行排序,取排序后的中间值替换图像当前窗口的中心值即可。
def medianBlur(src, ksize, dst=None):
参数简要说明:
src: 输入图像 ;
ksize: 滤波核大小;
dst: 输出图像;
简单调用OpenCV-Python滤波函数接口及其实现结果:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
image_path = './data/filter_input.jpg'
img = cv.imread(image_path)
start = cv.getTickCount()
dst = cv.medianBlur(img, 5)
end = cv.getTickCount()
print('filter algorithm time is ', (end-start)/cv.getTickFrequency(), ' seconds.')
show_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
plt.subplot(121), plt.imshow(show_img), plt.title('Original')
plt.xticks([]), plt.yticks([])
show_dst = cv.cvtColor(dst, cv.COLOR_BGR2RGB)
plt.subplot(122), plt.imshow(show_dst), plt.title('medianFilter')
plt.xticks([]), plt.yticks([])
plt.show()
高斯滤波
我们在未知图像中的噪声类别时候都会假设图像中的噪声为高斯噪声。高斯滤波作为最广泛图像滤波算法,其原因主要有如下几点:高斯滤波核函数线性可分、计算效率快、标准的尺度不变核函数等。
def GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None):
参数简要说明:
src: 输入图像;
ksize: 高斯核大小;
sigmaX:高斯核标准派生方差;
sigmaY: 默认设置为0,将会设置与sigmaX一致;如果sigmaX与sigmaY都为0,那么将会通过计算ksize.width与ksize.height来输出sigmaX与sigmaY大小;
borderType: 卷积在图像边缘的填充方式;
double sigmaX = sigma > 0 ? sigma : ((n-1)*0.5 - 1)*0.3 + 0.8;
其中,n为输入图像的宽,sigmaY对应的n就是图像的高;
dst: 输出图像;
简单调用OpenCV-Python滤波函数接口及其实现结果:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
image_path = './data/filter_input.jpg'
img = cv.imread(image_path)
kernel_size = (5, 5)
start = cv.getTickCount()
dst = cv.GaussianBlur(img, kernel_size, 0)
end = cv.getTickCount()
print('filter algorithm time is ', (end-start)/cv.getTickFrequency(), ' seconds.')
show_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
plt.subplot(121), plt.imshow(show_img), plt.title('Original')
plt.xticks([]), plt.yticks([])
show_dst = cv.cvtColor(dst, cv.COLOR_BGR2RGB)
plt.subplot(122), plt.imshow(show_dst), plt.title('GaussFilter')
plt.xticks([]), plt.yticks([])
plt.show()
双边滤波
双边滤波是基于上述算法无法有效的保存高频边缘信息的情况下研究出来的一种保持边缘的滤波算法。双边滤波考虑图像空间域(高斯核函数只考虑空间域)与像素范围域(值域)两个限制条件进行加权平均滤波。下面简单通过公式介绍双边滤波是如何进行保持边缘滤波的:
从公式3可以清晰理解双边滤波为什么具有保持边缘的效果。当滤波至边缘区域时,像素值 I ( i , j ) I(i,j) I(i,j)与 I ( k , l ) I(k,l) I(k,l)相差较大,这样使 w ( i , j , k , l ) w(i,j,k,l) w(i,j,k,l)滤波系数降低。当滤波至平坦区域时,像素值 I ( i , j ) I(i,j) I(i,j)与 I ( k , l ) I(k,l) I(k,l)几乎相等,那么此时的 w ( i , j , k , l ) w(i,j,k,l) w(i,j,k,l)几乎等价于高斯滤波,从而滤波系数大。
def bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType=None):
参数简要说明:
src: 输入图像;
d: 滤波核函数的邻域直径,如果是负数,则根据sigmaSpace来计算得出;
sigmaColor: 滤波色彩空间的sigma参数(值域);
sigmaSpace: 坐标空间的sigma参数(空域);
dst: 输出图像;
borderType: 图像卷积边界填充方式;
简单调用OpenCV-Python滤波函数接口及其实现结果:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
image_path = './data/filter_input.jpg'
img = cv.imread(image_path)
start = cv.getTickCount()
dst = cv.bilateralFilter(img, 9, 75, 75)
end = cv.getTickCount()
print('filter algorithm time is ', (end-start)/cv.getTickFrequency(), ' seconds.')
show_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
plt.subplot(121), plt.imshow(show_img), plt.title('Original')
plt.xticks([]), plt.yticks([])
show_dst = cv.cvtColor(dst, cv.COLOR_BGR2RGB)
plt.subplot(122), plt.imshow(show_dst), plt.title('bilateralFilter')
plt.xticks([]), plt.yticks([])
plt.show()
小结:
本篇博客目的在于简单介绍几种基本的图像滤波算法的OpenCV-Python应用,除去双边滤波其他的几种都是线性滤波算法。各滤波算法源码后期有空将会贴出来进行分析。这几种算法应用很广泛,同时也出现较早。后期出现一批保持边缘的滤波算法,滤波功能大幅提升,同时也一定程度提升的计算耗时。下一篇将会介绍保持边缘的滤波算法进一步发展的应用。