图像滤波是在尽量保留原图像细节特征的条件下,对该图像的噪声进行抑制,处理效果的好坏会直接影响后续操作的有效性。
消除图像中噪声数据的过程叫做图像滤波或图像平滑,图像中的有效信息主要集中在低频和中频区域,高频区域中的有效信息会被噪声数据影响。
图像滤波的目的: 消除图像中的噪声;为图像识别等后续操作提取中相应的特征。
图像滤波的要求: 尽可能地不破坏原图像中的特征(如边缘);视觉效果应更好。
(1)均值滤波
均值滤波是一种低通滤波器,高频信号将会被去掉,可消除图像中尖锐噪声,其思想是利用某一个像素点和它周围8邻域的像素值构建成一个算子,计算它们的均值,并用均值替换原图像该点的像素值。
(2)中值滤波
中值滤波是消除图像噪声较为常用的方法,处理椒盐噪声效果较好,其思想是利用某一个像素点和它周围8邻域的像素值构成一个算子,将它们排序后,并用中间的值替换原图像该点的像素值。
(3)最大最小值滤波
最大最小值滤波的思想是利用某一个像素点和它周围8邻域的像素值构成一个算子,将他们排序后,若中值比最大值大,将原图像该点像素值替换为该算子中的最大值,反之,替换为最小值,若处于最大值和最小值之间,可指定原图像该点的像素值为最大值/最小值。
(4)sobel算子
sobel算子是一个矩阵,主要用于图像中物体的边缘提取。
(5)高斯平滑
高斯平滑呈高斯分布,突出了中心点在像素平滑后的权重,有着较好的滤波效果,二维高斯公式如下:
(6)线性滤波
线性滤波的处理操作是利用权重矩阵(算子)与图像中某几个点对应相乘,并进行求和操作。(具体的讲解可参考下图的转载链接)
(图像转自https://blog.csdn.net/zouxy09/article/details/49080029,侵删)
(1)均值滤波
a.对原图像进行填充操作(padding)
b.取原图像的9个点作为kernel
c.求均值
d.更新像素值
e.直到循环结束
f.返回新图像
(2)中值滤波
a.对原图像进行填充操作(padding)
b.计算中位数位置
c.选取原图像的9个点作为kernel
d.排序
e.选取中间值更新原图像像素值
f.直到循环结束
g.返回新图像
(最大最小值滤波与中值滤波相似,此处不在重复说明)
(3)sobel算子
a.生成sobel矩阵
b.返回sobel矩阵
(4)高斯平滑
a.根据高斯公式计算高斯算子(其中x,y的计算方法可见https://blog.csdn.net/shawroad88/article/details/87935922)
b.进行线性滤波操作
(对高斯平滑加深理解可见https://www.zhihu.com/question/54918332)
(5)线性滤波
a.对原图像进行填充操作(padding)
b.截取图像中n*n个像素点与权重矩阵对应相乘
c.求和
d.更新像素值
e.对图像进行阶段操作(像素值大于255,令它为255,小于0,对该点像素进行abs操作)
f.直到循环结束
g.返回新图像
(1)均值滤波
def mean_filter(src,kernel_size,epoch):#均值滤波
kernel_height,kernel_weight=kernel_size
dst=src
pad_num=int(np.floor(kernel_weight/2))#计算需要加的边
pad_src=np.pad(src,((pad_num,pad_num),(pad_num,pad_num)),'constant')#加边
for z in range(epoch):#迭代
for i in range(pad_num,pad_src.shape[0]-pad_num):#滤波
for j in range(pad_num,pad_src.shape[1]-pad_num):
sum_temp=sum(pad_src[i-pad_num:i+pad_num+1,j-pad_num:j+pad_num+1]).tolist()#求和
#print(sum_temp)
sum_temp=sum(sum_temp)
#print(sum_temp)
temp=sum_temp/(kernel_weight*kernel_height)#求均值
dst[i-pad_num,j-pad_num]=temp#更新像素
src=dst#更新原图像
pad_src = np.pad(src, ((pad_num, pad_num), (pad_num, pad_num)), 'constant')
dst = dst.astype(np.uint8)
return dst
(2)中值滤波
def median_filter(src,kernel_size,epoch):#中值滤波
kernel_height,kernel_weight=kernel_size
pad_num=int(np.floor(kernel_height/2))#计算需要添加的边
center=int(np.round((kernel_height*kernel_weight)/2))#计算中位数
dst=src
pad_src=np.pad(src,((pad_num,pad_num),(pad_num,pad_num)),'constant')#加边
for z in range(epoch):#迭代
for i in range(pad_num, src.shape[0] - pad_num):#滤波
for j in range(pad_num, src.shape[1] - pad_num):
temp = pad_src[i - pad_num:i + pad_num + 1, j - pad_num:j + pad_num + 1].flatten().tolist()#获取n*n的矩阵并平展
temp = sorted(temp)#排序
dst[i - pad_num, j - pad_num] = temp[center]#更新像素值
src=dst#更新原图像
pad_src = np.pad(src, ((pad_num, pad_num), (pad_num, pad_num)), 'constant')
dst=dst.astype(np.uint8)
return dst
(3)sobel算子
def sobel_kernel():
sobel_horizontal=np.array([[1,2,1],[0,0,0],[-1,-2,-1]]).astype(np.uint8)#水平sobel算子
sobel_vertical=sobel_horizontal.T#垂直sobel算子
return sobel_horizontal,sobel_vertical
(4)高斯平滑
def guass_kernel(kernel_size,sigma):#创建高斯kernel
kernel_height,kernel_weight=kernel_size
kernel=np.zeros(kernel_size)#创建高斯kernel
for i in range(kernel_height):
for j in range(kernel_weight):
r=math.pow((i-(kernel_height-1)/2),2)#计算x
c=math.pow((j-(kernel_weight-1)/2),2)#计算y
kernel[i,j]=(1/(2*math.pi*math.pow(sigma,2)))*math.exp(-(math.pow(r,2)+math.pow(c,2))/(2*math.pow(sigma,2)))#按照高斯公式计算每一个kernel的值
sum_temp=sum(kernel)#求和
kernel=kernel/sum_temp#最终的高斯kernel
return kernel
(5)线性滤波
def sliding_window(src,kernel,stride):#用于单通道滑窗
dst = src
kernel_height,kernel_weight=kernel.shape#获取kernel的高度和宽度
exnum=int(np.floor(kernel_height/2))#填充的维数,floor函数向下取整
img_pad=np.pad(src,((exnum,exnum),(exnum,exnum)),'constant')#给图像加边,方便滑窗
for i in range(exnum,img_pad.shape[0]-exnum,stride):
for j in range(exnum,img_pad.shape[1]-exnum,stride):
temp=(img_pad[i-exnum:i+exnum+1,j-exnum:j+exnum+1]*kernel)/(kernel_height*kernel_weight)#截取n*n的mat与kenel对应相乘
sum_temp=sum(temp).tolist()#相加
sum_temp=sum(sum_temp)
img_pad[i,j]=sum_temp#更新像素
if img_pad[i,j]<0:#截断操作
img_pad[i,j]=abs(img_pad[i,j])
if img_pad[i,j]>255:
img_pad[i,j]=255
dst[i-exnum,j-exnum]=img_pad[i,j]
dst=dst.astype(np.uint8)
return dst
(6)测试代码
#均值滤波
img=cv2.imread("1.png",0)
dst=mean_filter(img,(3,3),5)
print(dst)
cv2.imshow("dst",dst)
cv2.waitKey(0)
#中值滤波
img=cv2.imread("1.png",0)
dst=mean_filter(img,(3,3),5)
print(dst)
cv2.imshow("dst",dst)
cv2.waitKey(0)
#高斯滤波
kernel=guass_kernel((3,3),1.5)
print(kernel)
img=cv2.imread("1.png",0)
dst=sliding_window(img,kernel,1)
cv2.imshow("dst",dst)
cv2.waitKey(0)
#sobel
sobel_kernel_horizontal,sobel_kernel_vertical=sobel_kernel()
img=cv2.imread("1.png",0)
dst_horizontal=sliding_window(img,sobel_kernel_horizontal,1)
dst_vertical=sliding_window(img,sobel_kernel_vertical,1)
dst=dst_horizontal+dst_vertical
cv2.imshow("dst_horizontal",dst_horizontal)
cv2.imshow("dst_vertical",dst_vertical)
cv2.imshow("dst",dst)
cv2.waitKey(0)
(1)均值滤波
优点:算法简单,计算速度较快。
缺点:降噪的同时会使得图像模糊,尤其是物体的边缘。
(2)中值滤波
优点:抑制噪声效果较好,图像的清晰度基本保持。
缺点:对高斯噪声的抑制效果不是很好。
(3)sobel
主要用于提取物体的边缘。