实验要求:
1)通过调整高斯函数的标准差(sigma)来控制平滑程度;
给定函数:void Gaussian(const MyImage &input, MyImage &output, double sigma);
2)滤波窗口大小取为[6sigma-1]/22+1,[.]表示取整;
3)利用二维高斯函数的行列可分离性进行加速;
先对每行进行一维
实验思路:
1.二维高斯滤波器的生成:模板的中心点是(center, center),根据G(x, y)高斯公式,其他点(i, j)有x = i – center; y = center – j 代入计算。这样求到了模板上每个位置的值,现在是小数。我们可以将模板左上角的像素值 v 乘以 1/v,这样左上角的元素就变为了1。模板的其他元素w也都乘以 1/v即int(w/v),这样所有元素都变为了整数。归一化时除以模板所有元素和就行。
2.一维高斯滤波器的生成也几乎是一样的。行列分离就是用一维高斯滤波器先对行操作,再对列操作,反过来也是一样的。
实验效果:
实验代码:
整个实验代码中包含两个函数Gaussian(未实现行列分离)和SeperateGaussian(实现了行列分离)。
采用行列分离一定要充分利用numpy的特性,否则可能会导致速度下降。以sigma等于1.03为例,Gaussian大约为6.26秒,SeperateGaussian如果仍是逐行逐列去遍历,时间是11秒左右,竟然比未行列分离慢,这显然是不可接受的。通过使用numpy的矩阵乘法,时间下降至0.07秒,如果将k也放入矩阵进行运算时间增加了0.015秒左右,即0.085秒左右。
import cv2 as cv
import numpy as np
def Gaussian(inputImage, outputImage, sigma):
timeBegin = cv.getTickCount()
# 产生二维高斯滤波器kernel,行列不分离
size = int(int(6*sigma-1)//2*2+1) # 高斯滤波器大小为:size x size,size为奇数
kernel = np.zeros([size, size], dtype=np.int)
center = size//2 # 将滤波器分为size x size个小方格,中心点为center,坐标为(0, 0)
normal = 1/(np.exp(-(2*center*center)/(2*(sigma*sigma)))) # 用于整数化
sumAll = 0 # 模板参数总和
for i in range(size):
for j in range(size):
x = i-center # 方格的横坐标
y = center-j # 方格的纵坐标
kernel[i, j] = int(np.exp(-(x*x+y*y)/(2*sigma*sigma)) * normal)
sumAll += kernel[i, j]
# print(kernel[i, j], end=' ') # 打印模板,与下一行共同使用
# print('') # 打印模板,与上一行共同使用
# 对图像inputImage增添
border = center # 需要添加的边界大小
transImage = cv.copyMakeBorder(inputImage, border, border, border, border,
borderType=cv.BORDER_REPLICATE) # 复制最边缘像素
# 开始平滑操作
rows, cols, channels = inputImage.shape
for i in range(rows):
for j in range(cols):
for k in range(channels):
temp = np.sum(np.multiply(transImage[i:i+size, j:j+size, k], kernel)) // sumAll
if temp < 0:
temp = 0
elif temp > 255:
temp = 255
outputImage[i, j, k] = temp
timeEnd = cv.getTickCount()
time = (timeEnd-timeBegin)/cv.getTickFrequency()
return time
def SeperateGaussian(inputImage, outputImage, sigma):
timeBegin = cv.getTickCount()
# 产生一维高斯滤波器kernel,行列分离
size = int(int(6*sigma-1)//2*2+1) # 高斯滤波器大小为:size x size,size为奇数
kernel = np.zeros([size], dtype=np.int)
center = size//2 # 将滤波器分为size x size个小方格,中心点为center,坐标为(0, 0)
normal = 1/(np.exp(-center*center/(2*(sigma*sigma)))) # 用于整数化
sumAll = 0 # 模板参数总和
for i in range(size):
kernel[i] = int(np.exp(-(i-center)*(i-center)/(2*sigma*sigma)) * normal)
sumAll += kernel[i]
#print(kernel[i], end=' ') # 打印模板
kernelRow = kernel
kernelCol = np.resize(kernel, (size, 1))
#print(kernelCol)
# 对图像inputImage增添
border = center # 需要添加的边界大小
transImage = cv.copyMakeBorder(inputImage, border, border, border, border,
borderType=cv.BORDER_REPLICATE) # 复制最边缘像素
# 开始平滑操作
rows, cols, channels = inputImage.shape
# 对行操作
for j in range(cols):
for k in range(channels):
temp = np.sum(np.multiply(transImage[:, j:j+size, k], kernelRow), axis=1) // sumAll
transImage[:, j+border, k] = temp
# 对列操作
for i in range(rows):
for k in range(channels):
temp = np.sum(np.multiply(transImage[i:i + size, border:cols + border, k], kernelCol), axis=0) // sumAll
outputImage[i, :, k] = temp
timeEnd = cv.getTickCount()
time = (timeEnd - timeBegin) / cv.getTickFrequency()
return time
sig = float(input('Please input the value of sigma: '))
print('Please choose the mode of Gaussian: ')
print(' Type 1 if you want to Gaussian')
print(' Type 2 if you want to SeperateGaussian')
print(' Type 3 if yuu want to both')
flag = int(input('The mode is: '))
imgSrc = cv.imread('../images/images2_1/a.jpg') # (481, 641, 3)
imgDst = np.zeros(list(imgSrc.shape), dtype='uint8')
time1 = 0 # 行列不分离的时间
time2 = 0 # 行列分离的时间
time = 0 # 两种方式的时间差
#print(imgSrc.shape)
if flag == 1:
time = Gaussian(imgSrc, imgDst, sig)
elif flag == 2:
time = SeperateGaussian(imgSrc, imgDst, sig)
elif flag == 3:
time1 = Gaussian(imgSrc, imgDst, sig)
time2 = SeperateGaussian(imgSrc, imgDst, sig)
strSigma = 'Gaussian image(sigma: ' + str(sig) + ')'
cv.imshow('source image', imgSrc)
cv.imshow(strSigma, imgDst)
saveSigma = str(sig) + '.png'
cv.imwrite(saveSigma, imgDst)
print("Successful!!!")
if flag == 1 or flag == 2:
print('time(s):', time)
elif flag == 3:
print('time1(s):', time1)
print('time2(s):', time2)
print('time2-time1 =', time2-time1)
cv.waitKey(0)
cv.destroyAllWindows()