【4】python-opencv3教程:图像平滑1(高斯平滑,快速均值平滑,中值平滑,双边滤波平滑)
第四节:图像平滑1
一:高斯平滑
通过以上步骤我们写一个构建高斯卷积算子的代码
def getGaussKernel(sigma, H, W):
# 第一步:构建高斯矩阵
gaussMatrix = np.zeros([H, W], np.float32)
# 得到中心点的位置
cH = (H - 1)/2
cW = (W - 1)/2
# 计算guass(sigma, r, c)
for r in range(H):
for c in range(W):
norm2 = math.pow(r-cH, 2) + math.pow(c-cW, 2)
gaussMatrix[r][c] = math.exp(-norm2/(2*math.pow(sigma, 2)))
# 第二步:计算高斯矩阵的和
sumGM = np.sum(gaussMatrix)
# 第三步:归一化
gaussKernel = gaussMatrix/sumGM
return gaussKernel
其实在真正的实践中,这个高斯卷积算子不用我们去实现,opencv给我们提供了函数cv2.getGaussianKernel()。。还有一点我想说一下,高斯算子是一个可分的算子,也就是分为一个子算子乘另一个子算子。。我们在做卷积的时候,图像可以先与水平高斯卷积核卷积,卷积出来的结果再与垂直方向的高斯卷积核卷积。。 我们做一个高斯卷积平滑的例子。。
import numpy as np
from scipy import signal
import cv2
def gaussBlur(image, sigma, H, W, _boundary = 'fill', _fillvalue=0):
# 构建水平方向上的高斯卷积核
gaussKernel_x = cv2.getGaussianKernel(sigma, W, cv2.CV_64F)
# 转置
gaussKernel_x = np.transpose(gaussKernel_x)
# 图像矩阵与水平高斯卷积核卷积
gaussBlur_x = signal.convolve2d(image, gaussKernel_x, mode='same', boundary=_boundary, fillvalue=_fillvalue)
gaussKernel_y = cv2.getGaussianKernel(sigma, H, cv2.CV_64F)
# 与垂直方向上的高斯卷积核卷积
gaussBlur_xy = signal.convolve2d(gaussBlur_x, gaussKernel_y, mode='same', boundary=_boundary, fillvalue=_fillvalue)
return gaussBlur_xy
if __name__ == '__main__':
image = cv2.imread('p1.jpg', cv2.IMREAD_GRAYSCALE)
# 显示原图
cv2.imshow('origin', image)
# 高斯平滑
blurImage = gaussBlur(image, 5, 51, 51, 'symm')
# 对blurImage进行灰度级显示
blurImage = np.round(blurImage)
blurImage = blurImage.astype(np.uint8)
cv2.imshow('smooth', blurImage)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出结果:
二:均值平滑+快速均值平滑
均值平滑
快速均值平滑
然后,图像积分完成后,计算某个位置的像素值,用下面的公式。。
下面是代码实现:
import numpy as np
import cv2
def integral(image):
'''
图像积分
:param image:
:return:
'''
rows, cols = image.shape
# 行积分运算
inteImageC = np.zeros((rows, cols), np.float32)
for r in range(rows):
for c in range(cols):
if c == 0:
inteImageC[r][c] = image[r][c]
else:
inteImageC[r][c] = inteImageC[r][c-1] + image[r][c]
# 列积分
interImage = np.zeros((rows, cols), np.float32)
for c in range(cols):
for r in range(rows):
if r == 0:
interImage[r][c] = inteImageC[r][c]
else:
interImage[r][c] = interImage[r-1][c] + inteImageC[r][c]
# 先行再列 或者 先列再行 积分的结果是一致的
# 上边和左边补零
interImage_0 = np.zeros((rows+1, cols+1), np.float32)
interImage_0[1:rows+1, 1:cols+1] = interImage
return interImage_0
# 快速均值滤波
def fastMeanBlur(image, winSize, borderType=cv2.BORDER_DEFAULT):
halfH = int((winSize[0] - 1) / 2)
halfW = int((winSize[1] - 1) / 2)
ratio = 1.0 / (winSize[0] * winSize[1])
# 边界扩充
paddImage = cv2.copyMakeBorder(image, halfH, halfH, halfW, halfW, borderType)
# 图像积分
paddIntegral = integral(paddImage)
# 获取图像的高宽
rows, cols = image.shape
# 均值滤波后的结果
meanBlurImage = np.zeros((rows, cols), np.float32)
r, c = 0, 0
for h in range(halfH, halfH+rows, 1): # 这里要想到边界扩充
for w in range(halfW, halfW+cols, 1):
meanBlurImage[r][c] = (paddIntegral[h+halfH+1][w+halfW+1] + paddIntegral[h-halfH][w-halfW]
-paddIntegral[h+halfH+1][w-halfW]-paddIntegral[h-halfH][w+halfW+1])*ratio
c += 1
r += 1
c = 0
return meanBlurImage
if __name__ == '__main__':
image = cv2.imread('p1.jpg', cv2.IMREAD_GRAYSCALE)
meanBlurImage = fastMeanBlur(image, (5, 5), borderType=cv2.BORDER_DEFAULT)
meanBlurImage = meanBlurImage.astype(np.uint8)
cv2.imshow('origin', image)
cv2.imshow('meanBlurImage', meanBlurImage)
# common_mean = common_mean_blur(image, (3, 3))
# common_mean = np.round(common_mean)
# common_mean = common_mean.astype(np.uint8)
# cv2.imshow('common_image', common_mean)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出结果:
三:中值平滑
中值平滑对有椒盐噪声的图片效果非常好。。所以我们下面将实验一下效果:先对图像加椒盐噪声,再对图像做中值平滑
代码实现:
import numpy as np
import random
import cv2
# 先加一点椒盐噪声 1000个白点,1000个黑点
def salt(image, number):
# 图像的宽,高
rows, cols = image.shape
# 加入椒盐噪声后的图像
saltImage = np.copy(image)
for i in range(number):
randR = random.randint(0, rows - 1)
randC = random.randint(0, cols - 1)
saltImage[randR][randC] = 255
for i in range(number):
randR = random.randint(0, rows - 1)
randC = random.randint(0, cols - 1)
saltImage[randR][randC] = 0
return saltImage
def medianBlur(image, winSize):
# 图像的高,宽
rows, cols = image.shape
# 窗口的宽高均为奇数
winH, winW = winSize
halfWinH = int((winH - 1) / 2)
halfWinW = int((winW - 1) / 2)
# 中值滤波后的输出图像
medianBlurImage = np.zeros(image.shape, image.dtype)
for r in range(rows):
for c in range(cols):
# 判断边界
rTop = 0 if r-halfWinH < 0 else r-halfWinH
rBottom = rows-1 if r+halfWinH > rows-1 else r+halfWinH
cLeft = 0 if c-halfWinW < 0 else c-halfWinW
cRight = cols if c+halfWinW > cols-1 else c+halfWinW
# 取邻域
region = image[rTop:rBottom+1, cLeft:cRight+1]
# 求中值
medianBlurImage[r][c] = np.median(region)
return medianBlurImage
if __name__ == '__main__':
image = cv2.imread('p1.jpg', cv2.IMREAD_GRAYSCALE)
saltImage = salt(image, 1000)
# 用中值滤波平滑加了椒盐噪声的图片
medianBlurImage = medianBlur(saltImage, (5, 5))
cv2.imshow('saltImage', saltImage)
cv2.imshow('meanImage', medianBlurImage)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出结果:
四:双边滤波平滑
代码实现:
import cv2
import numpy as np
import math
def getClosenessWeight(sigma_g, H, W):
# 计算空间距离权重模板
r, c = np.mgrid[0:H:1, 0:W:1] # 构造三维表
r -= int((H-1) / 2)
c -= int((W-1) / 2)
closeWeight = np.exp(-0.5*(np.power(r, 2)+np.power(c, 2))/math.pow(sigma_g, 2))
return closeWeight
def bfltGray(I, H, W, sigma_g, sigma_d):
# 构建空间距离权重模板
closenessWeight = getClosenessWeight(sigma_g, H, W)
# 模板的中心点位置
cH = int((H - 1) / 2)
cW = int((W - 1) / 2)
# 图像矩阵的行数和列数
rows, cols = I.shape
# 双边滤波后的结果
bfltGrayImage = np.zeros(I.shape, np.float32)
for r in range(rows):
for c in range(cols):
pixel = I[r][c]
# 判断边界
rTop = 0 if r-cH < 0 else r-cH
rBottom = rows-1 if r+cH > rows-1 else r+cH
cLeft = 0 if c-cW < 0 else c-cW
cRight = cols-1 if c+cW > cols-1 else c+cW
# 权重模板的作用区域
region = I[rTop:rBottom+1, cLeft:cRight+1]
# 构建灰度值相似性的权重因子
similarityWeightTemp = np.exp(-0.5*np.power(region-pixel, 2.0)/math.pow(sigma_d, 2))
closenessWeightTemp = closenessWeight[rTop-r+cH:rBottom-r+cH+1, cLeft-c+cW:cRight-c+cW+1]
# 两个权重模板相乘
weightTemp = similarityWeightTemp * closenessWeightTemp
# 归一化权重模板
weightTemp = weightTemp / np.sum(weightTemp)
# 权重模板和对应邻域值相乘求和
bfltGrayImage[r][c] = np.sum(region * weightTemp)
return bfltGrayImage
if __name__ == '__main__':
image = cv2.imread('p1.jpg', cv2.IMREAD_GRAYSCALE)
# 显示原图
cv2.imshow('origin', image)
# 将灰度值进行归一化
image = image / 255.0
# 双边滤波
bfltImage = bfltGray(image, 33, 33, 19, 0.2)
cv2.imshow('bfltImage', bfltImage)
cv2.waitKey(0)
cv2.destroyAllWindows()
双边滤波的代码运行不出来,不知道代码有问题还是电脑有问题? 大家复制过去自己跑一下。。。。