前几天“衣 紧 还 乡”,今天还是得把滤波算法补一下了
高斯滤波 参考blog:
小学的时候看我妈搞PS,有个叫“高斯模糊”的东西,今天终于明白了,这个高斯模糊,其实学名叫“高斯滤波”,是一种图像滤波算法,好神奇诶~
原理 对于通常的二维图像,高斯滤波使用二维正态分布函数(高斯函数) 作为权重分配,函数形式如下
其实这就是之前在学CNN时的图像卷积 操作(所以滤波器也叫做卷积核 ),通过构造一个卷积核,不断对原图的每个像素点进行卷积计算,对于一个3*3
的滤波器来说,就是将指定像素与周围8个像素点乘以3*3
的高斯权重分布再求和 得到该像素点的输出值
求高斯权重分布的方法:将滤波器中心点视为原点(0,0)
,可以得到滤波器函数值的分布,最后一定要再除以矩阵所有值之和 ,因为需要满足权重和为1,这是归一化操作所必需要有的步骤
PS:由于要保持滤波后的图像尺寸与原图相同,需要有$zero-padding$,即在矩阵周围补零,经过简单的数学推导,容易得出,在滤波器步长为1且滤波器为方阵的情况下,$pad$大小与滤波器尺寸$size$须满足如下关系:
效果
代码
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import cv2import numpy as npdef gauss_func (x,y,sigma ): return np.exp(-(x*x+y*y)/(2 *sigma*sigma))/(2 *np.pi*sigma*sigma)def gauss_filter (image,Size=3 ,sigma=1.3 ): img = image.copy() H,W,C = image.shape pad = Size//2 pad_img = np.zeros((H+pad*2 ,W+pad*2 ,C),dtype=np.float ) pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float ) Filter = np.zeros((Size,Size),dtype=np.float ) for i in range (-pad,-pad+Size): for j in range (-pad,-pad+Size): Filter[i+pad,j+pad] = gauss_func(j,i,sigma) Filter /= Filter.sum () out = np.zeros_like(img,dtype=np.float ) for i in range (H): for j in range (W): for c in range (C): out[i,j,c] = np.sum (Filter * pad_img[i:i+Size,j:j+Size,c]) out = np.clip(out,0 ,255 ) out = out.astype(np.uint8) return out img = cv2.imread("test.jpg" ) img2 = gauss_filter(img,Size=13 ,sigma=3 )print (img.shape) cv2.imshow("img2" , img2) cv2.imshow("img1" , img) cv2.waitKey(0 )
中值滤波 其实就是利用滤波器大小区域的原图对应元素的中位数 作为该像素点的输出值,那么代码只需要把高斯滤波模板稍微改改就行了(利用np.median()
)求中位数
效果
代码
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import cv2import numpy as npdef mid_filter (image,Size=3 ): img = image.copy() H,W,C = image.shape pad = Size//2 pad_img = np.zeros((H+pad*2 ,W+pad*2 ,C),dtype=np.float ) pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float ) out = np.zeros_like(img,dtype=np.float ) for i in range (H): for j in range (W): for c in range (C): out[i,j,c] = np.median(pad_img[i:i+Size,j:j+Size,c]) out = np.clip(out,0 ,255 ) out = out.astype(np.uint8) return out img = cv2.imread("test.jpg" ) img2 = mid_filter(img,Size=13 )print (img.shape) cv2.imshow("img2" , img2) cv2.imshow("img1" , img) cv2.waitKey(0 )
均值滤波 同上,不过这次是均值 ,代码就不放了
效果
Motion 滤波 取对角线方向像素的平均值作为输出,即 滤波器矩阵为(以3阶为例):
效果
代码
用np.diag()
方法生成对角阵,传入对角元素值作为参数即可
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import cv2import numpy as npdef motion_filter (image,Size=3 ): img = image.copy() H,W,C = image.shape pad = Size//2 pad_img = np.zeros((H+pad*2 ,W+pad*2 ,C),dtype=np.float ) pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float ) Filter = np.diag([1 ] * Size).astype(np.float ) Filter /= Size out = np.zeros_like(img,dtype=np.float ) for i in range (H): for j in range (W): for c in range (C): out[i,j,c] = np.sum (Filter * pad_img[i:i+Size,j:j+Size,c]) out = np.clip(out,0 ,255 ) out = out.astype(np.uint8) return out img = cv2.imread("test.jpg" ) img2 = motion_filter(img,Size=5 )print (img.shape) cv2.imshow("img2" , img2) cv2.imshow("img1" , img) cv2.waitKey(0 )
MIN-MAX 滤波 MAX-MIN滤波器使用网格内像素的最大值和最小值的差值 对网格内像素重新赋值。通常用于边缘检测 。
边缘检测用于检测图像中的线。像这样提取图像中的信息的操作被称为特征提取 。
边缘检测通常在灰度图像上进行。
效果 这里用filter_size = 3
的滤波器进行特征提取
代码
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import cv2import numpy as npdef minmax_filter (image,Size=3 ): img = image.copy() H,W,C = image.shape pad = Size//2 pad_img = np.zeros((H+pad*2 ,W+pad*2 ,C),dtype=np.float ) pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float ) out = np.zeros_like(img,dtype=np.float ) for i in range (H): for j in range (W): for c in range (C): out[i,j,c] = np.max (pad_img[i:i+Size,j:j+Size,c])-np.min (pad_img[i:i+Size,j:j+Size,c]) out = np.clip(out,0 ,255 ) out = out.astype(np.uint8) return out img = cv2.imread("test.jpg" ) img2 = minmax_filter(img,Size=5 )print (img.shape) cv2.imshow("img2" , img2) cv2.imshow("img1" , img) cv2.waitKey(0 )
差分滤波 差分滤波器对图像亮度急剧变化的边缘有提取效果,可以获得邻接像素的差值。
纵向: 横向:
因此,差分滤波有横向和纵向两种方式,分别能提取对应的边缘特征
效果 vout
为纵向,hout
为横向
代码
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import cv2import numpy as npdef differential_filter (image,Size=3 ): img = image.copy() H,W,C = image.shape pad = Size//2 pad_img = np.zeros((H+pad*2 ,W+pad*2 ,C),dtype=np.float ) pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float ) vFilter = np.array([[0 ,-1 ,0 ],[0 ,1 ,0 ],[0 ,0 ,0 ]]) hFilter = np.array([[0 ,0 ,0 ],[-1 ,1 ,0 ],[0 ,0 ,0 ]]) vout = np.zeros_like(img,dtype=np.float ) hout = vout.copy() for i in range (H): for j in range (W): for c in range (C): vout[i,j,c] = np.sum (vFilter * pad_img[i:i+Size,j:j+Size,c]) hout[i,j,c] = np.sum (hFilter * pad_img[i:i+Size,j:j+Size,c]) vout = np.clip(vout,0 ,255 ) vout = vout.astype(np.uint8) hout = np.clip(hout,0 ,255 ) hout = hout.astype(np.uint8) return hout,vout img = cv2.imread("test2.jpg" ) hout,vout = differential_filter(img,Size=3 ) out = hout+vout cv2.imshow("vout" , vout) cv2.imshow("hout" , hout) cv2.imshow("out" , out) cv2.imshow("img" , img) cv2.waitKey(0 )
Sobel 滤波 Sobel滤波器可以提取特定方向(纵向或横向)的边缘,滤波器按下式定义:
纵向: 横向:
PS:从矩阵就可以看出来,这其实就是类似于差分滤波的一种滤波方式,加剧图像差别大的边缘的差别,从而达到特征提取的效果。代码实现上,只要把差分滤波的滤波器矩阵换掉就行了
效果
Prewitt 滤波 Prewitt滤波器是用于边缘检测的一种滤波器,使用下式定义:
纵向: 横向:
PS:也类似于上面两种
效果
Laplacian 滤波 Laplacian滤波器是对图像亮度进行二次微分从而检测边缘的滤波器。由于数字图像是离散的,$x$方向和$y$方向的一次微分分别按照以下式子计算:
因此二次微分按照以下式子计算:
同理:
特此,Laplacian 表达式如下:
如果把这个式子表示为卷积核是下面这样的:
PS:代码其实也就是把上面的矩阵换掉即可
效果
Emboss 滤波 Emboss滤波器可以使物体轮廓更加清晰,按照以下式子定义:
PS:类似PS里面的浮雕效果,
效果
LoG滤波 LoG即高斯-拉普拉斯(Laplacian of Gaussian)的缩写,使用高斯滤波器使图像平滑化之后再使用拉普拉斯滤波器使图像的轮廓更加清晰。
为了防止拉普拉斯滤波器计算二次微分会使得图像噪声更加明显,所以我们首先使用高斯滤波器来抑制噪声。
LoG 滤波器使用以下式子定义:
PS:即 先降噪,再提取轮廓特征
效果
代码
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import cv2import numpy as npdef LoG_func (x,y,sigma=1.3 ): return (x*x+y*y-sigma*sigma)/(2 *np.pi*sigma**6 )*np.exp(-(x*x+y*y)/(2 *sigma*sigma))def LoG_filter (image,Size=3 ,sigma=1.3 ): img = image.copy() H,W,C = image.shape pad = Size//2 pad_img = np.zeros((H+pad*2 ,W+pad*2 ,C),dtype=np.float ) pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float ) Filter = np.zeros((Size,Size), dtype=np.float ) for y in range (-pad,-pad+Size): for x in range (-pad,-pad+Size): Filter[y+pad,x+pad] = LoG_func(x,y,sigma) Filter /= Filter.sum () out = np.zeros_like(img,dtype=np.float ) for i in range (H): for j in range (W): for c in range (C): out[i,j,c] = np.sum (Filter * pad_img[i:i+Size,j:j+Size,c]) out = np.clip(out,0 ,255 ) out = out.astype(np.uint8) return out img = cv2.imread("test2.jpg" ) out = LoG_filter(img) cv2.imshow("out" , out) cv2.imshow("img" , img) cv2.waitKey(0 )