目录
一、常见的颜色空间
二、图像卷积
三、噪声
1、高斯噪声(gaussian)
2、椒盐噪声(salt&pepper)
四、图像滤波
1、线性滤波
(1)均值滤波
(2)方框滤波
(3)高斯滤波
2、非线性滤波
(1)中值滤波
(2)双边滤波
1.RGB(最常用的,Red,Green,Blue)
2.HSI (色调、饱和度、明度)
3.YUV (“Y”表示明亮度,“U”和“V”表示的则是色度,常用于电视信号系统)
4.CMYK(用于印刷,Cyan = 青色,Magenta = 品红色,Magenta = 品红色,blacK=黑色)
5.GRAY(灰度图,只有一个通道,由0-255表示,0表示最黑,255表示最白)
GRAY = 0.3R + 0.59G + 0.11B
注:
1、图片的格式为[h, w, c]( RGB 和 BGR 都是)
所以需要转换通道:
import cv2
img = cv2.imread('bird.jpg') # img.shape = (607, 500, 3)
img = img.transpose((2,0,1)) # img.shape = (3, 607, 500)
2、OpenCV采用的是BGR,所以在使用时需要进行转换。
一般有两种方式:
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_rgb = img[: , : , : : -1]
卷积操作,就是将原图与卷积核(通常选用5*5,3*3的卷积核)先从左上角开始对齐,对应的点进行相乘再相加,然后将结果输出到原图像与卷积模板中心对应的像素处。
注:卷积模板若不是中心对称的,需要翻转180°。使用cv.flip()函数
import cv2 as cv
import numpy as np
def run():
src = np.array([[1,2,3,4,5],
[6,7,8,9,10],
[11,12,13,14,15],
[16,17,18,19,20],
[21,22,23,24,25]],dtype='float32')
kernel = np.array([[1,1,1],
[1,1,1],
[1,1,1]],dtype='float32') / 9
result = cv.filter2D(src, -1, kernel=kernel)
dst = cv.flip(src,-1) # 180°翻转
print('卷积前矩阵:\n{}'.format(src))
print('翻转后的src:\n{}'.format(dst))
print('卷积后矩阵:\n{}'.format(result))
img = cv.imread('./bird/bird.jpg')
kernel2 = np.ones((7,7), np.float32) / 49
result2 = cv.filter2D(img, -1, kernel=kernel2) # -1代表输出图像的数据类型与输入图像的相同
cv.imshow('Origin IMage', img)
cv.imshow('Filter Result',result2)
cv.waitKey(0)
cv.destroyWindow()
if __name__ == '__main__':
run()
输出结果:
卷积前矩阵:
[[ 1. 2. 3. 4. 5.]
[ 6. 7. 8. 9. 10.]
[11. 12. 13. 14. 15.]
[16. 17. 18. 19. 20.]
[21. 22. 23. 24. 25.]]
翻转后的src:
[[25. 24. 23. 22. 21.]
[20. 19. 18. 17. 16.]
[15. 14. 13. 12. 11.]
[10. 9. 8. 7. 6.]
[ 5. 4. 3. 2. 1.]]
卷积后矩阵:
[[ 5. 5.3333335 6.3333335 7.333333 7.666667 ]
[ 6.666667 7. 8. 9. 9.333333 ]
[11.666668 12. 13. 13.999999 14.333334 ]
[16.666666 17. 17.999998 19. 19.333332 ]
[18.333334 18.666666 19.666668 20.666668 21. ]]
图1. 经过7*7卷积后的图像(右)
定义:噪声在图像上常表现为一引起较强视觉效果的孤立像素点或像素块。一般,噪声信号与要研究的对象不相关,它以无用的信息形式出现,扰乱图像的可观测信息。
高斯噪声是指它的概率密度函数服从高斯分布(即正态分布)的一类噪声。如果一个噪声,它的幅度分布服从高斯分布,而它的功率密度谱又是均匀分布的,则称它为高斯白噪声。高斯白噪声的二阶矩不相关,一阶矩为常数,是指先后信号在时间上的相关性。
add_gaussian_noisy.py 添加高斯噪声代码如下
import cv2 as cv
import numpy as np
def add_noise(image, mean=0, val=0.01):
size = image.shape
image = image / 255 # 对图像进行归一化,范围[0,1]
gauss = np.random.normal(mean, val ** 0.5, size) # 高斯分布的平均值、标准差、输出的随机数据的维度
noise = image + gauss
return gauss, noise
if __name__ == '__main__':
img = cv.imread('./bird/bird.jpg')
# 转换为灰度图,再添加高斯噪声
gray_image = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gray_gauss, gray_noisy_image = add_noise(gray_image)
# 为彩色图像添加噪声
color_gauss, color_noisy_image = add_noise(img)
cv.imshow("Gray Image", gray_image)
# cv.imshow("Gray Gauss Image", gray_gauss)
cv.imshow("Gray Noisy Image", gray_noisy_image)
cv.imshow("Color Image", img)
# cv.imshow("Color Gauss Image", color_gauss)
cv.imshow("Color Noisy Image", color_noisy_image)
cv.waitKey(0)
cv.destroyWindow()
图2. 为灰度图添加高斯噪声(右)
图3. 为彩色图添加高斯噪声(右)
椒盐噪声又称脉冲噪声,会随机改变图像中的像素值,是由相机、传输通道、解码处理等产生的黑白相间的亮暗点噪声。
add_peppersalt_noisy.py 添加椒盐噪声代码如下:
import cv2 as cv
import numpy as np
def add_noisy(image, n=10000):
result = image.copy()
w, h = image.shape[:2]
for i in range(n):
# 分别在宽和高的范围内生成一个随机值,模拟代表(x,y)的坐标
x = np.random.randint(1, w)
y = np.random.randint(1, h)
if np.random.randint(0, 2) == 0:
# 生成白色噪声
result[x, y] = 0
else:
# 生成黑色噪声、
result[x, y] = 255
return result
if __name__ == '__main__':
# 读取图像
img = cv.imread('./bird/bird.jpg')
# 为灰度图像添加椒盐噪声
gray_image = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gray_image_noisy = add_noisy(gray_image)
# 为彩色图像添加噪声
color_image_noisy = add_noisy(img)
# 展示结果
cv.imshow("Gray Image", gray_image)
cv.imshow("Gray Image Noisy", gray_image_noisy)
cv.imshow("Color Image", img)
cv.imshow("Color Image Noisy", color_image_noisy)
cv.waitKey(0)
cv.destroyWindow()
图4. 为灰度图添加椒盐噪声(右)
图5. 为彩色图添加椒盐噪声(右)
图像滤波是指去除图像中不重要的内容而使关心的内容表现得更加清晰的方法。根据图像滤波的目的,图像滤波分为消除图像噪声的滤波和提取图像中部分特征信息的滤波。去除图像中的噪声叫作图像的平滑或者图像去噪。
均值滤波将滤波器内所有的像素值都看作中心像素值的测量值,将滤波器所有像素值的平均值作为滤波器中心的像素值。计算公式如下:
blur.py 对图像进行均值滤波
import cv2 as cv
def my_blur(image):
return cv.blur(image, (3, 3)), cv.blur(image, (9, 9)) # cv.blur()均值滤波
if __name__ == "__main__":
img = cv.imread('./bird/gray_bird.jpg') # 灰度图
img_sp = cv.imread('./bird/gray_bird_peppersalt.jpg') # 添加了椒盐噪声的灰度图
img_gauss = cv.imread('./bird/gray_bird_gaussian.jpg') # 添加了高斯噪声的灰度图
img1, img2 = my_blur(img)
img_sp1, img_sp2 = my_blur(img_sp)
img_gauss1, img_gauss2 = my_blur(img_gauss)
# 展示结果
cv.imshow("Origin Image",img)
cv.imshow("3*3 Blur Image",img1)
cv.imshow("9*9 Blur Image",img2)
cv.imshow("Origin sp-noisy Image",img_sp)
cv.imshow("3*3 sp-noisy Blur Image",img_sp1)
cv.imshow("9*9 sp-noisy Blur Image",img_sp2)
cv.imshow("Origin gauss-noisy Image",img_gauss)
cv.imshow("3*3 gauss-noisy Blur Image",img_gauss1)
cv.imshow("9*9 gauss-noisy Blur Image",img_gauss2)
cv.waitKey(0)
cv.destroyWindow()
图6. blur.py程序中不含噪声图像的均值滤波处理
图7. blur.py程序中含椒盐噪声图像的均值滤波处理
图8. blur.py程序中含高斯噪声图像的均值滤波处理
方框滤波是均值滤波的一般形式。方框滤波也会求滤波器内所有像素值的和,但是方框滤波可以选择不进行归一化,而将所有像素值的和作为滤波结果,而不是所有像素值的平均值。
注:方框滤波的函数 cv.boxFilter(),其中第6个参数为normalize,表示是否对滤波器内所有的数值进行归一化操作,默认为True。此时,若不考虑数据类型,函数 cv.boxFilter() 和 cv.blur() 有相同的过滤效果。除了cv.boxFilter()以外,OpenCV4还提供了cv.sqrBoxFilter()函数,用于对滤波器内每个像素值的平方求和。
boxFilter.py 对图像进行方框滤波
import cv2 as cv
import numpy as np
if __name__ == "__main__":
img = cv.imread('./bird/gray_bird.jpg')
# 验证方框滤波算法的矩阵
points = np.array([[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25]],dtype='float32')
# 将图像转为 float32 类型的矩阵
# img_32 = img.astype('float32')
# img_32 /= 255.0 # 归一化
# 方框滤波 图像对比
# cv.boxFilter() 方框滤波函数 cv.sqrBoxFilter() 取平方和的方框滤波函数
# 进行归一化
img_box_norm = cv.boxFilter(img, -1, (3, 3), anchor=(-1, -1), normalize=True)
img_sqr_norm = cv.sqrBoxFilter(img, -1, (3, 3), anchor=(-1, -1),
normalize=False, borderType=cv.BORDER_CONSTANT)
# 不进行归一化
img_box = cv.boxFilter(img, -1, (3, 3), anchor=(-1, -1), normalize=False)
# 方框滤波 矩阵对比
# 进行归一化
points_sqr_norm = cv.sqrBoxFilter(points, -1, (3, 3), anchor=(-1, -1),
normalize=True, borderType=cv.BORDER_CONSTANT)
# 不进行归一化
points_sqr = cv.sqrBoxFilter(points, -1, (3, 3), anchor=(-1, -1),
normalize=False, borderType=cv.BORDER_CONSTANT)
# 展示结果
print("未归一化的数据:",points_sqr)
print("归一化的数据:",points_sqr_norm)
cv.imshow("Result(cv.boxFilter() NORM)",img_box_norm)
cv.imshow("Result(cv.boxFilter())",img_box)
cv.imshow("Result(cv.sqrBoxFilter() NORM)",img_sqr_norm / np.max(img_sqr_norm))
cv.waitKey(0)
cv.destroyWindow()
# 未归一化的结果
[[ 90. 163. 223. 295. 222.]
[ 355. 597. 732. 885. 643.]
[ 895. 1452. 1677. 1920. 1363.]
[1735. 2757. 3072. 3405. 2383.]
[1470. 2323. 2563. 2815. 1962.]]
#归一化的结果
[[ 10. 18.11111111 24.77777778 32.77777778 24.66666667]
[ 39.44444444 66.33333333 81.33333333 98.33333333 71.44444444]
[ 99.44444444 161.33333333 186.33333333 213.33333333 151.44444444]
[192.77777778 306.33333333 341.33333333 378.33333333 264.77777778]
[163.33333333 258.11111111 284.77777778 312.77777778 218. ]]
图9.boxFilter.py 程序中图像的方框滤波结果
未进行归一化的图像在经过方框滤波后,大部分的像素值在uint8数据类型下为255。
高斯滤波器考虑了像素与滤波器中心距离的影响,以滤波器为中心为高斯分布的均值,根据高斯分布公式和每个像素与中心的距离计算处滤波器内每个位置的数值,从而形成一个高斯滤波器。
图10.高斯滤波器(来源: 百度百科)
gaussianBlur.py 对图像进行高斯滤波
import cv2 as cv
if __name__ == "__main__":
# 读取照片
img = cv.imread('./bird/gray_bird.jpg')
img_gauss = cv.imread('./bird/gray_bird_gaussian.jpg', cv.IMREAD_ANYDEPTH)
img_salt = cv.imread('./bird/gray_bird_peppersalt.jpg', cv.IMREAD_ANYDEPTH)
# 分别对上述图像进行高斯滤波, 后面的数组代表滤波器大小
result_5 = cv.GaussianBlur(img, (5, 5), 10, 20) # cv.GaussianBlur() 高斯滤波函数
result_9 = cv.GaussianBlur(img, (9, 9), 10, 20)
result_5_gauss = cv.GaussianBlur(img_gauss, (5, 5), 10, 20)
result_9_gauss = cv.GaussianBlur(img_gauss, (9, 9), 10, 20)
result_5_salt = cv.GaussianBlur(img_salt, (5, 5), 10, 20)
result_9_salt = cv.GaussianBlur(img_salt, (9, 9), 10, 20)
# 展示图像
cv.imshow("Origin img", img)
cv.imshow("Result img 5*5", result_5)
cv.imshow("Result img 9*9", result_9)
cv.imshow("Origin img_gauss", img_gauss)
cv.imshow("Result img_gauss 5*5", result_5_gauss)
cv.imshow("Result img_gauss 9*9", result_9_gauss)
cv.imshow("Origin img_salt", img_salt)
cv.imshow("Result img_salt 5*5", result_5_salt)
cv.imshow("Result img_salt 9*9", result_9_salt)
cv.waitKey(0)
cv.destroyWindow()
图11. gaussianBlur.py 程序中不含噪声图像的高斯滤波结果
图12. gaussianBlur.py 程序中含高斯噪声图像的高斯滤波结果
图13. gaussianBlur.py 程序中含椒盐噪声图像的高斯滤波结果
为什么要采用非线性滤波?
在线性滤波中,如果想消除图像中的噪声,需要用滤波器中系数为0的元素乘以这个噪声。但是线性滤波的结果只是将所有像素值的线性组合,因此含有噪声的像素也会被考虑进去,噪声并不会消除。因此引出非线性滤波器。
中值滤波就是用滤波器范围内所有像素值的中值来替代滤波器中心位置的像素值,是一种基于排序统计理论的能够有效抑制噪声的非线性信号处理方法。
方法:将滤波器范围内的所有像素值按照由小到大的顺序排列,取排序序列的中值作为滤波器中心的新像素值,之后将滤波器移动到下一个位置,重复排序与取中值的操作,直到将图像中所有的像素都与滤波器中心对应一遍。
medianBlur.py 对图像进行中值滤波
import cv2 as cv
if __name__ == "__main__":
# 读取图片
img = cv.imread('./bird/color_bird_peppersalt.jpg')
gray = cv.imread('./bird/gray_bird_peppersalt.jpg', cv.IMREAD_ANYDEPTH)
# 分别对含椒盐噪声的彩色图像和灰度图像进行中值滤波,后面的数字代表滤波器大小
img_3 = cv.medianBlur(img, 3) # cv.medianBlur() 中值滤波函数
gray_3 = cv.medianBlur(gray, 3)
# 增大 ksize ,图像会变模糊
img_9 = cv.medianBlur(img, 9)
gray_9 = cv.medianBlur(gray, 9)
# 展示结果
cv.imshow('Origin img', img)
cv.imshow('img 3*3', img_3)
cv.imshow('img 9*9', img_9)
cv.imshow('Origin gray', gray)
cv.imshow('img 3*3', gray_3)
cv.imshow('img 9*9', gray_9)
cv.waitKey(0)
cv.destroyWindow()
图14. medianBlur.py 程序中灰度图像中值滤波结果
图15. medianBlur.py 程序中彩色图像中值滤波结果
双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折中处理,同时考虑空域信息和灰度相似性,可以在保留区域信息的基础上实现噪声的去除、局部边缘的平滑。双边滤波器是空域(domain)滤波器和值域滤波器(range domain)滤波器的结合。
双边滤波器的数学表达式如下:
其中,表示输入图像,表示输出图像,为双边滤波器的系数,其取值决定于空域滤波器和值域滤波器的乘积。
注:双边滤波器函数 cv.bilateralFilter(src, d, sigmaColor, sigmaSpace)
sigmaColor : 颜色空间滤波器的标准差,数值越大,像素邻域内越多的颜色被混合到一起,会产生将达的颜色混合区域
sigmaSpace : 空间坐标中滤波器的标准差,数值越大,越远的像素会互相影响,从而使更大邻域中有足够相似的像素获取相同的颜色。
bilateralFilter.py 对图像进行双边滤波
import cv2 as cv
if __name__ == "__main__":
# 读取照片
img = cv.imread('./bird/face.jpg', cv.IMREAD_ANYDEPTH)
# 验证不同滤波器直径的滤波效果
res1 = cv.bilateralFilter(img, 9, 50, 25 / 2) # cv.bilateralFilter() 双边滤波函数
res2 = cv.bilateralFilter(img, 25, 50, 25 / 2)
# 验证不同标准差的滤波效果
res3 = cv.bilateralFilter(img, 9, 9, 9)
res4 = cv.bilateralFilter(img, 9, 200, 200)
cv.imshow('Origin_image',img)
cv.imshow('Result1', res1)
cv.imshow('Result2', res2)
cv.imshow('Result3', res3)
cv.imshow('Result4', res4)
cv.waitKey(0)
cv.destroyWindow()
原图(来源:百度):
图16. bilateralFilter.py 程序中不同滤波器直径的滤波效果
图17. bilateralFilter.py 程序中不同标准差值的滤波效果