import cv2 as cv
import numpy as np
#传入灰度图像
#sigma是高斯分布的标准差,avr是均值
def makeNoise(img,sigma,avr):
img2=img.copy()#使用副本,不用原本的
row,col=img2.shape#row和col都是列表类型,代表行数,列数
for x in range(0,row):
for y in range(0,col):
img2[x,y] = img2[x,y]+np.random.normal(avr,sigma**2,1)[0]
return img2
#传入灰度图像
#kernel_size为奇数。sigmax为0时是程序根据公式计算得方差,不为0时代表指定标准差为sigmax
def guassianImgFilt(img,kernel_size,sigmax):
row,col = img.shape#获得未添加边界前的大小信息
#下面产生卷积核
kernel = np.zeros([kernel_size,kernel_size])
center = kernel_size//2#整除
#计算标准差
if sigmax == 0:
sigma = ((kernel_size-1)*0.5-1)*0.3+0.8
else:
sigma = sigmax
s = 2*(sigma**2)
sum_val = 0
for i in range(0,kernel_size):
for j in range(0,kernel_size):
x = i-center
y = j-center#center-j也无所谓,反正权重是按到圆心距离算的,而且距离带平方了,正负无所谓,x**2+y**2的值代表了权重。
kernel[i,j] = np.exp(-(x**2+y**2)/s)
sum_val += kernel[i,j]
#/(np.pi * s)
sum_val = 1/sum_val
#对卷积核归一化,确保卷积核之和为1
kernel = kernel*sum_val#对于np.array来说是对应位置相乘。这里不要用除,最好是用乘以分之一的形式
#以上是产生卷积核
#计算图像边界需要添加的范围,扩充图像边界使得能遍历到原图像的每一个像素
addLine = int((kernel_size-1)/2)#虽然addLine理应是整数,但存储类型是浮点,要转换类型
img = cv.copyMakeBorder(img,addLine,addLine,addLine,addLine,borderType=cv.BORDER_REPLICATE)
#定位未扩充之前图像左上角元素在新图像中的下标索引,这个索引将用来遍历原图像的每一个像素点,相当于指针
source_x = addLine#定义起始位置,即未扩充之前图像左上角元素在新图像中的下标索引
source_y = addLine#定义起始位置,即未扩充之前图像左上角元素在新图像中的下标索引
#addLine的值是单边添加边界的大小(行数,也是列数),一共四个边添加边界
#在添加了边界后的图像中遍历未添加边界时的原像素点,进行滤波
#外层控制行,内层控制列
for delta_x in range(0,row):
for delta_y in range(0,col):
img[source_x,source_y] = np.sum(img[source_x-addLine:source_x+addLine+1,source_y-addLine:source_y+addLine+1]*kernel)
source_y = source_y+1
source_x = source_x+1#行加1,准备进行下行的所有列的遍历
source_y = addLine#把列归位到原始的列起点准备下轮列遍历
#经过上面的循环后,图像已经滤波完成了
#剥除四边添加的边界,然后返回滤波后的图片
return img[addLine:row+addLine,addLine:col+addLine]
#-----------------------------------------------------------------------------------------------------#
img = cv.imread("D:\\OpenCV\\testimg\\lena.png",cv.IMREAD_UNCHANGED)#图像读入后是BGR形式
imgGray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)#BGR图像转为灰度图像
imgGrayWithNosie = makeNoise(imgGray,3,0)
imgfilted = guassianImgFilt(imgGrayWithNosie,5,0)
cv.imshow("origin",img)
cv.imshow("gray",imgGray)
cv.imshow("GrayWithNosie",imgGrayWithNosie)
cv.imshow("filted",imgfilted)
cv.waitKey()
cv.destroyAllWindows()
如果对于卷积和卷积核不太理解的话可以参考我以前的一篇博客:
点击这里查看
声明:原本是在word中写的本篇博客,里面有mathtype敲出来的公式(CSDN不支持mathtype),转换为LaTex公式后放到博客里排版太乱了,所以就导出了PDF,分页输出图片,把图片放到博客里,这样排版效果更好一些。如果觉得大小不太合适可以按住CTRL键滑动鼠标滑轮缩放。本博客所用代码附在最后