图像滤波
要求
实现卷积
基于Python实现函数完成用滤波器h对灰度图像img0进行滤波,返回
滤波后的图像img1,即
i m g 1 = m y I m a g e F i l t e r ( i m g 0 , h ) img1 = myImageFilter(img0, h) img1=myImageFilter(img0,h)
要求滤波后的图像img1与原始输入图像具有相同的尺寸,滤波器h每个维度的大小为奇数(2k + 1, k = 1, 2, · · · )。在进行图像填充的时候可以使用课堂上讲过的任何一种填充方式均可。可以调用Python中NumPy的填充函数,但不能调用任何包括convolve, correlate, fftconvolve等函数(仅可以使用他们来与你自己的实现结果进行对比)。函数的实现需采用矢量化,关于矢量化的例子可参考https://www.pythonlikeyoumeanit.com/Module3_IntroducingNumpy/VectorizedOperations.html.
(提示:尽可能减少for循环的个数)
实现
分析步骤:
- 创建高斯滤波卷积核,并做归一化处理
- 获取图片的形状(高、宽、通道数)
- 图片零填充处理,得到图片副本
- 遍历图片副本所有像素点,将卷积核与图片副本的切片进行点乘求和运算,得到输出图片
- 输出图片切片,恢复原图形状
创建高斯滤波卷积核
高斯滤波器将中心像素周围的像素按照高斯分布加权平均进行平滑化。
按下面的高斯分布公式计算权值:
g
(
x
,
y
,
σ
)
=
1
2
π
σ
2
e
−
x
2
+
y
2
2
σ
2
g(x,y,\sigma) = \frac{1}{2\pi\sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2}}
g(x,y,σ)=2πσ21e−2σ2x2+y2
并对权值
g
g
g进行归一化操作。
标准差
σ
\sigma
σ=1.3的8−近邻高斯滤波器如下:
k
=
1
16
[
1
2
1
2
4
2
1
2
1
]
k = \frac{1}{16} \begin{bmatrix} 1 & 2 &1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \\ \end{bmatrix}
k=161⎣⎡121242121⎦⎤
示例
def gaussian_kernal_maker(k_size, sigma):
"""
Create a Gaussian kernel
:param k_size: size of the Gaussian kernel
:param sigma: standard deviation
:return k: Gaussian kernel
"""
pad = k_size // 2
k = np.zeros((k_size, k_size), dtype=np.float)
for x in range(-pad, -pad + k_size):
for y in range(-pad, -pad + k_size):
k[y + pad, x + pad] = np.exp(-(x ** 2 + y ** 2) / (2 * (sigma ** 2)))
k /= (2 * np.pi * sigma * sigma)
k /= k.sum()
return k
零填充
滤波后的图像img1需要与原始输入图像具有相同的尺寸,但是由于图像的长宽可能不是卷积核大小的整数倍,因此我们需要在图像的边缘补上
1
2
\frac{1}{2}
21 倍卷积核宽度的0像素点。
示例
# Zero padding
k_size = k.shape[0]
pad = k_size // 2
img1 = np.zeros((h + pad * 2, w + pad * 2, c), dtype=np.float)
img1[pad: pad + h, pad: pad + w] = img0.copy().astype(np.float)
代码
import numpy as np
import cv2 as cv
def myImageFilter(img0, k):
# YOUR CODE HERE
"""
Filtering using a given kernel k
:param img0: image
:param k: kernel
:return img1: filtered image
"""
# Get the image shape.h:height w:width c:channels
if len(img0.shape) == 3:
h, w, c = img0.shape
else:
img0 = np.expand_dims(img0, axis=-1)
h, w, c = img0.shape
# Zero padding
k_size = k.shape[0]
pad = k_size // 2
img1 = np.zeros((h + pad * 2, w + pad * 2, c), dtype=np.float)
img1[pad: pad + h, pad: pad + w] = img0.copy().astype(np.float)
# Filtering
tmp = img1.copy()
# Traversing the pixels of the image
for y in range(h): # h:height
for x in range(w): # w:width
for i in range(c): # c:channels
img1[pad + y, pad + x, i] = np.sum(k * tmp[y: y + k_size, x: x + k_size, i])
img1 = np.clip(img1, 0, 255)
img1 = img1[pad: pad + h, pad: pad + w].astype(np.uint8)
return img1
def gaussian_kernal_maker(k_size, sigma):
"""
Create a Gaussian kernel
:param k_size: size of the Gaussian kernel
:param sigma: standard deviation
:return k: Gaussian kernel
"""
pad = k_size // 2
k = np.zeros((k_size, k_size), dtype=np.float)
for x in range(-pad, -pad + k_size):
for y in range(-pad, -pad + k_size):
k[y + pad, x + pad] = np.exp(-(x ** 2 + y ** 2) / (2 * (sigma ** 2)))
k /= (2 * np.pi * sigma * sigma)
k /= k.sum()
return k
if __name__ == '__main__':
# Read image
# image = cv.imread("../image/example.png")
image = cv.imread("../image/example_gray.png")
cv.imshow("input", image)
# Create Gaussian kernel
kernel = gaussian_kernal_maker(k_size=3, sigma=1.3)
# Gaussian Filter
image_filtered = myImageFilter(img0=image, k=kernel)
# Save result
# cv.imwrite("output.jpg", image_filtered)
cv.imshow("output", image_filtered)
cv.waitKey(0)
cv.destroyAllWindows()
结果
input
output