高斯滤波和双边滤波的概念和实现
高斯滤波
高斯滤波比较简单,首先来看一下在写程序时用到的的二维高斯分布:
这里注意前面的常数我们省略了,因为在在计算高斯核的mask的时候,每一个对应位置的像素点都要乘这个常数,最后在归一化后,起到作用的只是权重之间的比值,这样说可能不是很清楚,用一张图来表示:
参考上面的公式,(0,0)点的位置取x=y=0,很明显,(0,0)点位置的权重最大这是因为二维高斯分布中,越靠近中心的位置,概率密度函数越大,有中心往四周递减,就比如(1,0)到(0,0)的距离是1,而(1,1)到(0,0)的距离是
2
\sqrt{2}
2,所以(1,0)点的权重就要比(0,0)点更大,值得注意的是滤波后的图像的某一点处对应的像素值是在原图像上面的高斯核覆盖的图像的加权相加,所以这里要对权值做归一化,就是让上图中9个点出的权值加加在一起等于1
得到根据公式计算出高斯核后,无脑在原图像上扫就可以了,注意如果不对原图像进行填充,得到的图像的长和宽都是要比原图像少一个(高斯核长度-1)的,具体实现过程放在下面的代码中了
双边滤波
由于高斯核在滤波的过程中只关心一个像素周围的像素到该像素的距离,并不关心该像素和周围的像素的大小关系,所以有点无脑模糊的意思,双边滤波就是将一个像素和周围像素之间的大小关系考虑进来了,这里我们简单的说就是当一个像素旁边的像素的像素值和这个中心像素差的很远的时候,我们就要把这个值差的很远的像素的权重减小,因为这个像素很可能和中心像素点不在一个“语义”上面,如果给它的权值设置的很大,就会将中心像素拉入错误的语义,这样的语义交融就是我们所说的“模糊”,双边滤波的实现也不难,基本思想也是用一个“核”在原图上面扫,只不过这个核k=k1*k2,k1就是高斯核,k2是:
这里的f(i,j)就是中心点的像素值,f(k,l)就是中心点周围某个点(k,l)的像素值可以看到,(k,l)核中心点差的越大,他对应的权值就越小
实现汇总
基于python+numpy的简单实现,运行速度一般,kernel_size搞大了跑的会比较慢,为了方便看用了进度条,没这个包的朋友可以自己在python环境里面pip install tqdm安装一下,还需要PIL的支持,没有的pip install scikit-image安装一下,numpy就不多赘述了
高斯滤波
#mask和图像上的像素无关,直接生成mask
def GenerateGaussianMask(kernel_size=3, sigma=0.8):
mask = [[[0.,0.,0.] for i in range(kernel_size)] for j in range(kernel_size)]
sum, center_x, center_y = 0, (kernel_size - 1) / 2, (kernel_size - 1) / 2
for i in range(kernel_size):
for j in range(kernel_size):
tem = (1 / 2 * PI * sigma ** 2) * \
(math.exp(-(((i - center_x) ** 2 + (j - center_y) ** 2) / 2 * sigma ** 2)))
mask[i][j]=[tem,tem,tem]
sum += tem
mask=np.array(mask)
mask/=[sum]
return mask
#高斯滤波
def GaussianFilt(img, kernel_size=9, sigma=0.2):
mask = GenerateGaussianMask(kernel_size, sigma)
img_np = np.asarray(img)
print(img_np.shape)
img_gf_np = np.zeros((img_np.shape[0] - kernel_size + 1, img_np.shape[1] - kernel_size + 1,3))
for i in tqdm(range(img_gf_np.shape[0])):
for j in range(img_gf_np.shape[1]):
tem=img_np[i:i+kernel_size,j:j+kernel_size,:]*mask
tem=np.sum(np.sum(tem,axis=0,keepdims=False),axis=0,keepdims=False)
img_gf_np[i][j] = tem
img_gf = Image.fromarray(np.uint8(img_gf_np))
img_gf.show()
return
双边滤波
这里注意需要用到上面GenerateGaussianMask函数,都复制到一个文件里面就好
#生成双边滤波的mask
def GenerateBilateralMask(region,mask_s,sigma_r=10):
kernel_size=region.shape[0]
mask_r = [[[0., 0., 0.] for i in range(kernel_size)] for j in range(kernel_size)]
sum, center_x, center_y = 0, int((kernel_size - 1) / 2), int((kernel_size - 1) / 2)
for i in range(kernel_size):
for j in range(kernel_size):
tem=math.exp(-((np.sum(region[i][j])-np.sum(region[center_x][center_y]))**2/(2*sigma_r**2)))
mask_r[i][j]=[tem,tem,tem]
sum+=(tem*mask_s[i][j][0])
mask=mask_r*mask_s/[sum]
return np.sum(np.sum(mask*region,axis=0,keepdims=False),axis=0,keepdims=False)
#双边滤波
def BilateralFilt(img,kernel_size=9,sigma_s=0.2,sigma_r=30):
mask_s=GenerateGaussianMask(kernel_size,sigma_s)
img_np = np.asarray(img,dtype=float)
print(img_np.shape)
img_bf_np = np.zeros((img_np.shape[0] - kernel_size + 1, img_np.shape[1] - kernel_size + 1, 3))
for i in tqdm(range(img_bf_np.shape[0])):
for j in range(img_bf_np.shape[1]):
region=img_np[i:i+kernel_size,j:j+kernel_size,:]
img_bf_np[i][j]=GenerateBilateralMask(region,mask_s,sigma_r)
img_gf = Image.fromarray(np.uint8(img_bf_np))
img_gf.show()
return
看一下最终效果:
原图:
高斯滤波:
双边滤波: