项目位置:OpenCV-Sample
代码位置:37-MedianBlur.py
先看一下效果吧,如图显示:
第一列:原图 第二列:自己手写的中值滤波 第三列 OpenCV的中值滤波.
原图 | 手写 | openCV |
---|
肉眼参看非常的接近.但效率上仍然无法与OpenCV匹敌.一个天上一个地下.以学习的目的进行手写.
原理比较简单,对消除椒盐噪点效果是非常的好.上面的图就是一个充满了椒盐噪点的图片.
传入原始图像
如果图片是一个4x4
的矩阵,kernel是一个3x3
的矩阵,kernel会在图片上进行滑动.
假设这个就是原始图形
[[6 4 8 1]
[2 5 4 5]
[2 3 7 6]
[8 6 6 6]]
进行Padding处理
因为kernel是3x3
的,所以要进行padding处理 padding = kernel//2
处理完的图像是:
[[0 0 0 0 0 0]
[0 6 4 8 1 0]
[0 2 5 4 5 0]
[0 2 3 7 6 0]
[0 8 6 6 6 0]
[0 0 0 0 0 0]]
Kernel在图像时滑动取值
然后就是kernel在图片上滑动了,第一次平移取到的数值.
[[0 0 0] [[0 0 0] [[0 0 0] [[0 0 0]
第一个取到的->[0 6 4] 第二个取到的-> [6 4 8] 第三个取到的-> [4 8 1] 第四个取到的-> [8 1 0]
[0 2 5]] [2 5 4]] [5 4 5]] [4 5 0]]
中值为4
将取到数值进行展开
第一个为[0 0 0 0 6 4 0 2 5 ], 第二为[ 0 0 0 6 4 8 2 5 4 ], 第三个为[0 0 0 4 8 1 5 4 5], 第四个为0 0 0 8 1 0 4 5 0]
对数值进行排序呢
第一个为[0 0 0 0 0 2 4 5 6 ], 第二为[0 0 0 2 4 4 5 6 8 ], 第三个为[0 0 0 1 4 4 5 5 8], 第四个为[0 0 0 0 0 1 4 5 8]
取中值
我都是去矩阵中的最中间的那个,也有取数值最接近平均数的那个.
取出的中值分别为第一个是0
, 第二个是4
,第三个是4
, 第四个是0
.
通过这些中值合成新的图片,详细代码如下:
import cv2
import numpy as np
def medianBlur(img, kernel, padding_way='ZERO', Multithreading=False):
# 检测传入的kernel是否为一个合法数组.
if kernel % 2 == 0 or kernel is 1:
print('kernel size need 3, 5, 7, 9....')
return None
# 通过kernel的大小来计算paddingSize的大小
paddingSize = kernel // 2
# 获取图片的通道数
layerSize = len(img.shape)
# 获取传入的图片的大小
height, width = img.shape[:2]
# 假设输入,如下矩阵,5x5
# [[2 6 3 4 7]
# [6 1 7 1 5]
# [4 6 7 3 3]
# [3 1 8 8 6]
# [2 4 8 0 7]]
# 这里是对多通道的处理
if layerSize == 3: # 多通道的处理方式,就是反复的调用单通道.
matMutbase = np.zeros_like(img)
for l in range(matMutbase.shape[2]):
matMutbase[:, :, l] = medianBlur(img[:, :, l], kernel, padding_way)
return matMutbase
elif layerSize == 2: # 单通道是中值滤波的实际工作的位置.
# 实现方式和np.lib.pad相同
# matBase = np.lib.pad(img,paddingSize, mode='constant', constant_values=0)
matBase = np.zeros((height + paddingSize * 2, width + paddingSize * 2), dtype=img.dtype)
# 创建一个添加了padding的矩阵,初始值为0
# 如果kernel的大小为3,所以从5x5变成了7x7
# [[0 0 0 0 0 0 0]
# [0 0 0 0 0 0 0]
# [0 0 0 0 0 0 0]
# [0 0 0 0 0 0 0]
# [0 0 0 0 0 0 0]
# [0 0 0 0 0 0 0]
# [0 0 0 0 0 0 0]]
matBase[paddingSize:-paddingSize, paddingSize:-paddingSize] = img
# 将原值写入新创建的矩阵当中
# [[0 0 0 0 0 0 0]
# [0 2 6 3 4 7 0]
# [0 6 1 7 1 5 0]
# [0 4 6 7 3 3 0]
# [0 3 1 8 8 6 0]
# [0 2 4 8 0 7 0]
# [0 0 0 0 0 0 0]]
# print(matBase)
if padding_way is 'ZERO':
pass
elif padding_way is 'REPLICA':
for r in range(paddingSize):
matBase[r, paddingSize:-paddingSize] = img[0, :]
matBase[-(1 + r), paddingSize:-paddingSize] = img[-1, :]
matBase[paddingSize:-paddingSize, r] = img[:, 0]
matBase[paddingSize:-paddingSize, -(1 + r)] = img[:, -1]
# 通过REPLICA后的矩阵,讲四个边补齐
# [[0 2 6 3 4 7 0]
# [2 2 6 3 4 7 7]
# [6 6 1 7 1 5 5]
# [4 4 6 7 3 3 3]
# [3 3 1 8 8 6 6]
# [2 2 4 8 0 7 7]
# [0 2 4 8 0 7 0]]
# 实现方式和np.lib.pad相同
# matBase = (img, padding, mode='edge')
# print(matBase)
else:
print('padding_way error need ZERO or REPLICA')
return None
# 创建用于输出的矩阵
matOut = np.zeros((height, width), dtype=img.dtype)
# 这里是遍历矩阵的每个点
for x in range(height):
for y in range(width):
# 获取kernel X kernel 的内容,并转化成队并列
line = matBase[x:x + kernel, y:y + kernel].flatten()
# 队列排序处理.
line = np.sort(line)
# 取中间值赋值
matOut[x, y] = line[(kernel * kernel) // 2]
return matOut
else:
print('image layers error')
return None
def main():
# 读取原始图片
img = cv2.imread("res/jyan.jpeg")
# print(img)
# 使用自己手写的medianBlur进行中值滤波啊
myself = medianBlur(img, 5, padding_way='REPLICA')
if myself is None:
return
# 调用OpenCV的接口进行中值滤波
opencv = cv2.medianBlur(img, 3)
# 这里进行图片合并
img = np.hstack((img, myself))
img = np.hstack((img, opencv))
# 显示对比效果
cv2.imshow('ORG + myself + OpenCV', img)
cv2.waitKey()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()