高斯滤波是以距离为权重,设计滤波模板作为滤波系数,只考虑了像素间的空间位置上的关系,因此滤波的结果会丢失边缘的信息。
高斯滤波的缺陷如下图所示:平坦区域正常滤波,图像细节没有变化,而在突变的边缘上,因为只使用了距离来确定滤波权重,导致边缘被模糊。
在高斯基础上,进一步优化,叠加了像素值的考虑,因此也就引出了双边滤波,一种非线性滤波,滤波效果对保留边缘更有效。
空间距离:当前点距离滤波模板中心点的欧式距离。
灰度距离:当前点距离滤波模板中心点的灰度的差值的绝对值。
双边滤波的核函数是空间域核与像素范围域核的综合结果:
1)在图像的平坦区域,像素值变化很小,那么像素差值接近于0,对应的像素范围域权重接近于1,此时空间域权重起主要作用,相当于进行高斯模糊;
2)在图像的边缘区域,像素值变化很大,那么像素差值大,对应的像素范围域权重变大,即使距离远空间域权重小,加上像素域权重总的系数也较大,从而保护了边缘的信息。
双边滤波的效果如下图,在突变的边缘上,使用了像素差权重,所以很好的保留了边缘。
import cv2
import numpy as np
image = cv2.imread('./image/cat.jpeg')
image_blur = cv2.blur(image, (3, 3))
image_bilater = np.hstack([ # 结果图像的水平拼接
image_blur,
cv2.bilateralFilter(image_blur, 5, 21, 21),
cv2.bilateralFilter(image_blur, 7, 31, 31),
cv2.bilateralFilter(image_blur, 9, 41, 41)
])
cv2.imshow('image_bilater', image_bilater)
cv2.waitKey(0)
引导滤波的思想用一张引导图像产生权重,从而对输入图像进行处理,这个过程可以表示为下面公式:
公式中 q、I、p分表表示输出图像、引导图像和输入图像 ,i、j分别表示图像中像素点的索引。可以看到上方公式中权重 W 仅与引导图像 I 有关,而在双边滤波中权重 W 由输入图像自身决定。
"""
uv通道滤波,取出颜色噪声
"""
import cv2
import numpy as np
image_path = './image/wait_uv_filter.png'
image = cv2.imread(image_path)
cv2.namedWindow('image', 0)
cv2.resizeWindow('image', 600, 500)
cv2.imshow('image', image)
cv2.waitKey(0)
def my_guidedFilter_oneChannel(srcImg, guidedImg, rad=9, eps=0.01):
srcImg = srcImg / 255.0
guidedImg = guidedImg / 255.0
P_mean = cv2.boxFilter(srcImg, -1, (rad, rad), normalize=True)
I_mean = cv2.boxFilter(guidedImg, -1, (rad, rad), normalize=True)
I_square_mean = cv2.boxFilter(np.multiply(guidedImg, guidedImg), -1, (rad, rad), normalize=True)
I_mul_P_mean = cv2.boxFilter(np.multiply(srcImg, guidedImg), -1, (rad, rad), normalize=True)
var_I = I_square_mean - np.multiply(I_mean, I_mean)
cov_I_P = I_mul_P_mean - np.multiply(I_mean, P_mean)
a = cov_I_P / (var_I + eps)
b = P_mean - np.multiply(a, I_mean)
a_mean = cv2.boxFilter(a, -1, (rad, rad), normalize=True)
b_mean = cv2.boxFilter(b, -1, (rad, rad), normalize=True)
dstImg = np.multiply(a_mean, guidedImg) + b_mean
return dstImg * 255.0
def my_guidedFilter_threeChannel(srcImg, guidedImg, rad=9, eps=0.01):
img_shape = np.shape(srcImg)
dstImg = np.zeros(img_shape, dtype=float)
for ind in range(0, img_shape[2]):
dstImg[:, :, ind] = my_guidedFilter_oneChannel(srcImg[:, :, ind],guidedImg[:, :, ind], rad, eps)
dstImg = dstImg.astype(np.uint8)
return dstImg
def main():
img = cv2.imread(input_fn)
print(np.shape(img))
img_y = cv2.split(img)[0]
img_u = cv2.split(img)[1]
img_v = cv2.split(img)[2]
image_guidedFilter = my_guidedFilter_threeChannel(img, img, 15, 0.0001)
print(np.shape(image_guidedFilter))
cv2.namedWindow('image_guidedFilter', 0)
cv2.resizeWindow('image_guidedFilter', 600, 500)
cv2.imshow('image_guidedFilter', image_guidedFilter)
cv2.waitKey(0)
dst_u = my_guidedFilter_oneChannel(img_u, img_u, 9, 0.0001).astype(np.uint8)
dst_v = my_guidedFilter_oneChannel(img_v, img_v, 9, 0.0001).astype(np.uint8)
dstimg_uv = cv2.merge([img_y, dst_u, dst_v])
cv2.namedWindow('dstimg_uv', 0)
cv2.resizeWindow('dstimg_uv', 600, 500)
cv2.imshow('dstimg_uv', dstimg_uv)
cv2.waitKey(0)
if __name__ == '__main__':
main()