7、灰度图
将彩色图像转换为灰度图像的过程称为灰度化,这种做法在图像处理和计算机视觉领域非常常见。
灰度图与彩色图最大的不同就是:彩色图是由R、G、B三个通道组成,而灰度图只有一个通道,也称为单通道图像,所以彩色图转成灰度图的过程本质上就是将R、G、B三通道合并成一个通道的过程。本实验中一共介绍了三种合并方法,分别是最大值法、平均值法以及加权均值法。
7.1灰度图
每个像素只有一个采样颜色的图像,这类图像通常显示为从最暗黑色到最亮的白色的灰度,尽管理论上这个采样可以任何颜色的不同深浅,甚至可以是不同亮度上的不同颜色。灰度图像与黑白图像不同,在计算机图像领域中黑白图像只有黑色与白色两种颜色;但是,灰度图像在黑色与白色之间还有许多级的颜色深度。灰度图像经常是在单个电磁波频谱如可见光内测量每个像素的亮度得到的,用于显示的灰度图像通常用每个采样像素8位的非线性尺度来保存,这样可以有256级灰度。
7.2最大值法
对于彩色图像的每个像素,它会从R、G、B三个通道的值中选出最大的一个,并将其作为灰度图像中对应位置的像素值。
例如某图像中某像素点的像素值如上图所示,那么在使用最大值法进行灰度化时,就会从该像素点对应的RGB通道中选取最大的像素值作为灰度值,所以在灰度图中的对应位置上,该像素点的像素值就是121。
# 灰度化:最大值法:图像大小不变,像素点个数不变,通道数由3变为1,像素值会变,变成三个通道里的最大值 pig=cv.imread("images/pig.png") # 获取原图大小形状 h,w,_=pig.shape # 创建一个和原图一样大小的图像,放像素,拿这张图就是灰度化后的图 gray=np.zeros((h,w),dtype=np.uint8) # 遍历原图,取出每个像素点,拿到三个通道里的最大像素值,放进创建的图像里 # 遍历行 for i in range(h): # 遍历列 for j in range(w): # 取出每个像素点 img[i,j] [0,1,2] gray[i,j]=max(pig[i,j][0],pig[i,j][1],pig[i,j][2]) gray[i,j]=max(pig[i,j]) cv.imshow("pig",pig) cv.imshow("gray",gray) cv.waitKey(0) cv.destroyAllWindows()
7.3平均值法
对于彩色图像的每个像素,它会将R、G、B三个通道的像素值全部加起来,然后再除以三,得到的平均值就是灰度图像中对应位置的像素值。
例如某图像中某像素点的像素值如上图所示,那么在使用平均值进行灰度化时,其计算结果就是(91+121+46)/3=86(对结果进行取整),所以在灰度图中的对应位置上,该像素点的像素值就是86。
# 灰度化:最大值法:图像大小不变,像素点个数不变,通道数由3变为1,像素值会变,变成三个通道里的最大值 pig=cv.imread("images/pig.png") pig=cv.resize(pig,(480,480)) # 获取原图大小形状 h,w,_=pig.shape # 创建一个和原图一样大小的图像,放像素,拿这张图就是灰度化后的图 gray=np.zeros((h,w),dtype=np.uint8) # 遍历原图,取出每个像素点,拿到三个通道里的最大像素值,放进创建的图像里 # 遍历行 for i in range(h): # 遍历列 for j in range(w): # 取出每个像素点 img[i,j] [0,1,2] gray[i,j]=np.uint8((int(pig[i,j,0])+int(pig[i,j,1])+int(pig[i,j,2]))//3) cv.imshow("pig",pig) cv.imshow("gray",gray) cv.waitKey(0) cv.destroyAllWindows()
7.4加权均值法
对于彩色图像的每个像素,它会按照一定的权重去乘以每个通道的像素值,并将其相加,得到最后的值就是灰度图像中对应位置的像素值。本实验中,权重的比例为: R乘以0.299,G乘以0.587,B乘以0.114,这是经过大量实验得到的一个权重比例,也是一个比较常用的权重比例。
所使用的权重之和应该等于1。这是为了确保生成的灰度图像素值保持在合理的亮度范围内,并且不会因为权重的比例不当导致整体过亮或过暗。
例如某图像中某像素点的像素值如上图所示,那么在使用加权平均值进行灰度化时,其计算结果就是10*0.299+121*0.587+46*0.114=79。所以在灰度图中的对应位置上,该像素点的像素值就是79。
# 灰度化:最大值法:图像大小不变,像素点个数不变,通道数由3变为1,像素值会变,变成三个通道里的最大值 pig=cv.imread("images/pig.png") pig=cv.resize(pig,(480,480)) # 获取原图大小形状 h,w,_=pig.shape # 定义三个通道的权重 wb,wg,wr=0.144,0.587,0.299 # 创建一个和原图一样大小的图像,放像素,拿这张图就是灰度化后的图 gray=np.zeros((h,w),dtype=np.uint8) # 遍历原图,取出每个像素点,拿到三个通道里的最大像素值,放进创建的图像里 # 遍历行 for i in range(h): # 遍历列 for j in range(w): # 取出每个像素点 img[i,j] [0,1,2] # R:0.299 G:0.587 B:0.114 gray[i,j]=np.round((pig[i,j,0]*wb+pig[i,j,1]*wg+pig[i,j,2]*wr).astype(np.uint8)) cv.imshow("pig",pig) cv.imshow("gray",gray) cv.waitKey(0) cv.destroyAllWindows()
7.5两个极端的灰度值
在灰度图像中,“极端”的灰度值指的是亮度的两个极端:最暗和最亮的值。
-
最暗的灰度值:0。这代表完全黑色,在灰度图像中没有任何亮度。
-
最亮的灰度值:255。这代表完全白色,在灰度图像中具有最大亮度。
8、图像二值化处理
将某张图像的所有像素改成只有两种值之一。
二值图像
一幅二值图像的二维矩阵仅由0、1两个值构成,“0”代表黑色,“1”代白色。由于每一像素(矩阵中每一元素)取值仅有0、1两种可能,所以计算机中二值图像的数据类型通常为1个二进制位。二值图像通常用于文字、线条图的扫描识别(OCR)和掩膜图像的存储。
其操作的图像也必须是灰度图。也就是说,二值化的过程,就是将一张灰度图上的像素根据某种规则修改为0和maxval(maxval表示最大值,一般为255,显示白色)两种像素值,使图像呈现黑白的效果,能够帮助我们更好地分析图像中的形状、边缘和轮廓等特征。
-
简便:降低计算量和计算需求,加快处理速度。
-
节约资源:二值图像占用空间远小于彩色图。
-
边缘检测:二值化常作为边缘检测的预处理步骤,因为简化后的图易于识别出轮廓和边界。
全局阈值法 :
_,binary = cv2.threshold(img,thresh,maxval,type)
-
img
:输入图像,要进行二值化处理的灰度图。 -
thresh
:设定的阈值。当像素值大于(或小于,取决于阈值类型)thresh
时,该像素被赋予的值。 -
type
:阈值处理的类型。 -
返回值:
-
第一个值(通常用下划线表示):计算出的阈值,若使用自适应阈值法,会根据算法自动计算出这个值。
-
第二个值(binary):二值化后的图像矩阵。与输入图像尺寸相同。
-
8.1阈值法(THRESH_BINARY )
阈值法就是通过设置一个阈值,将灰度图中的每一个像素值与该阈值进行比较,小于等于阈值的像素就被设置为0(通常代表背景),大于阈值的像素就被设置为maxval(通常代表前景)。对于我们的8位图像(0~255)来说,通常是设置为255。
flower=cv.imread("images/flower.png") flower_r=cv.resize(flower,(480,480)) gray=cv.cvtColor(flower_r,cv.COLOR_BGR2GRAY) # 二值化:阈值法 _,binary=cv.threshold(gray,127,255,cv.THRESH_BINARY) # cv.imshow("flower_r",flower_r) cv.imshow("gray",gray) cv.imshow("binary",binary) cv.waitKey(0) cv.destroyAllWindows()
8.2反阈值法(THRESH_BINARY_INV )
顾名思义,就是与阈值法相反。反阈值法是当灰度图的像素值大于阈值时,该像素值将会变成0(黑),当灰度图的像素值小于等于阈值时,该像素值将会变成maxval。
flower=cv.imread("images/flower.png") flower_r=cv.resize(flower,(480,480)) gray=cv.cvtColor(flower_r,cv.COLOR_BGR2GRAY) # 二值化:反阈值法 _,binary_inv=cv.threshold(gray,127,255,cv.THRESH_BINARY_INV) # cv.imshow("flower_r",flower_r) cv.imshow("gray",gray) cv.imshow("binary_inv",binary_inv) cv.waitKey(0) cv.destroyAllWindows()
8.3截断阈值法(THRESH_TRUNC )
截断阈值法,指将灰度图中的所有像素与阈值进行比较,像素值大于阈值的部分将会被修改为阈值,小于等于阈值的部分不变。
换句话说,经过截断阈值法处理过的二值化图中的最大像素值就是阈值。
flower=cv.imread("images/flower.png") flower_r=cv.resize(flower,(480,480)) gray=cv.cvtColor(flower_r,cv.COLOR_BGR2GRAY) # 二值化:截断阈值法 _,binary_trunc=cv.threshold(gray,127,255,cv.THRESH_TRUNC) # cv.imshow("flower_r",flower_r) cv.imshow("gray",gray) cv.imshow("binary_trunc",binary_trunc) cv.waitKey(0) cv.destroyAllWindows()
8.4低阈值零处理法(THRESH_TOZERO )
低阈值零处理,字面意思,就是像素值小于等于阈值的部分被置为0(也就是黑色),大于阈值的部分不变。
flower=cv.imread("images/flower.png") flower_r=cv.resize(flower,(480,480)) gray=cv.cvtColor(flower_r,cv.COLOR_BGR2GRAY) # 二值化:低阈值零处理 _,binary_zero=cv.threshold(gray,127,255,cv.THRESH_TOZERO) # cv.imshow("flower_r",flower_r) cv.imshow("gray",gray) cv.imshow("binary_zero",binary_zero) cv.waitKey(0) cv.destroyAllWindows()
8.5超阈值零处理(THRESH_TOZERO_INV )
超阈值零处理就是将灰度图中的每个像素与阈值进行比较,像素值大于阈值的部分置为0(也就是黑色),像素值小于等于阈值的部分不变。
flower=cv.imread("images/flower.png") flower_r=cv.resize(flower,(480,480)) gray=cv.cvtColor(flower_r,cv.COLOR_BGR2GRAY) # 二值化:超阈值零处理 _,binary_zero_inv=cv.threshold(gray,127,255,cv.THRESH_TOZERO_INV) # cv.imshow("flower_r",flower_r) cv.imshow("gray",gray) cv.imshow("binary_zero_inv",binary_zero_inv) cv.waitKey(0) cv.destroyAllWindows()
8.6OTSU阈值法
cv2.THRESH_OTS 并不是一个有效的阈值类型或标。THRESH_OTSU
本身并不是一个独立的阈值化方法,而是与 OpenCV 中的二值化方法结合使用的一个标志。具体来说,THRESH_OTSU
通常与 THRESH_BINARY
或 THRESH_BINARY_INV
结合使用。在实际应用中,如果你使用 THRESH_OTSU
标志但没有指定其他二值化类型,默认情况下它会与 THRESH_BINARY
结合使用。也就是说,当你仅指定了 cv2.THRESH_OTSU
,实际上等同于同时指定了 cv2.THRESH_BINARY + cv2.THRESH_OTSU
。
OTSU算法是通过一个值将这张图分前景色和背景色。使用最大类间方差计算得到的值最大时,该值就是二值化算法中所需要的阈值。通常该值是从灰度图中的最小值加1开始进行迭代计算,直到灰度图中的最大像素值减1,然后把得到的最大类间方差值进行比较,来得到二值化的阈值。
注意:使用OTSU算法计算阈值时,组件中的thresh参数将不再有任何作用。
flower=cv.imread("images/flower.png") flower_r=cv.resize(flower,(480,480)) gray=cv.cvtColor(flower_r,cv.COLOR_BGR2GRAY) # OTSU阈值法:结合阈值法和反阈值法,自动选择阈值 thresh,otus=cv.threshold(gray,127,255,cv.THRESH_OTSU) # cv.imshow("flower_r",flower_r) cv.imshow("gray",gray) cv.imshow("otus",otus) cv.waitKey(0) cv.destroyAllWindows()
8.7自适应二值化
与二值化算法相比,自适应二值化更加适合用在明暗分布不均的图片,因为图片的明暗不均,导致图片上的每一小部分都要使用不同的阈值进行二值化处理,这时候传统的二值化算法就无法满足我们的需求了,于是就出现了自适应二值化。
自适应二值化方法会对图像中的所有像素点计算其各自的阈值,这样能够更好的保留图片里的一些信息。
cv2.adaptiveThreshold(image_np_gray, maxval, cv2.ADAPTIVE_THRESH_MEAN_C, 二值化方法, blocksize, c)
参数:
maxval
:最大阈值,一般为255
adaptiveMethod
:小区域阈值的计算方式:
ADAPTIVE_THRESH_MEAN_C
:小区域内取均值
ADAPTIVE_THRESH_GAUSSIAN_C
:小区域内加权求和,权重是个高斯核
thresholdType
:二值化方法,只能使用THRESH_BINARY、THRESH_BINARY_INV,也就是阈值法和反阈值法
blockSize
:选取的小区域的面积,如7就是7*7的小块。
c
:最终阈值等于小区域计算出的阈值再减去此值
1.取均值
假如我们使用的小区域是3*3的,那么就会从图片的左上角开始(也就是像素值为162的地方)计算其邻域内的平均值,如果处于边缘地区就会对边界进行填充,填充值就是边界的像素点,如下图所示:
自适应二值化(Adaptive Thresholding)的核心思想就是为图像中的每个像素点计算一个局部阈值。这种方法与全局阈值化不同,后者对整个图像使用同一个固定的阈值。而在自适应二值化中,每个像素的阈值是基于其周围邻域内的像素值动态确定的。
flower=cv.imread("images/flower.png") flower_r=cv.resize(flower,(480,480)) gray=cv.cvtColor(flower_r,cv.COLOR_BGR2GRAY) # 自适应二值化:均值法 img_mean=cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,5,10) # cv.imshow("flower_r",flower_r) cv.imshow("gray",gray) cv.imshow("img_mean",img_mean) cv.waitKey(0) cv.destroyAllWindows()
2.加权求和
对小区域内的像素进行加权求和得到新的阈值,其权重值来自于高斯分布。
当核的大小不同时,仅仅是核的参数会发生变化,计算过程与此是一样的。
flower=cv.imread("images/flower.png") flower_r=cv.resize(flower,(480,480)) gray=cv.cvtColor(flower_r,cv.COLOR_BGR2GRAY) # 自适应二值化:加权均值法 img_guss=cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY_INV,3,10) # cv.imshow("flower_r",flower_r) cv.imshow("gray",gray) cv.imshow("img_guss",img_guss) cv.waitKey(0) cv.destroyAllWindows()
9、图像掩膜
9.1制作掩膜
掩膜(Mask)是一种在图像处理中常见的操作,它用于选择性地遮挡图像的某些部分,以实现特定任务的目标。掩膜通常是一个二值化图像,并且与原图像的大小相同,其中目标区域被设置为1(或白色),而其他区域被设置为0(或黑色),并且目标区域可以根据HSV的颜色范围进行修改,如下图就是制作红色掩膜的过程:
mask=cv.inRange(img,color_low,color_high)
-
cv2.inRange用于进行多通道图像(尤其是彩色图像)的阈值操作。
9.2与运算
“与”运算,其规则是当两个命题都是真时,其结果才为真。从上面的图片我们可以看出,掩膜中有很多地方是黑色的,其像素值为0,那么在与原图像进行“与”运算的时候,得到的新图像的对应位置也是黑色的,如下图所示:
通过掩膜与原图的与运算,我们就可以提取出图像中被掩膜覆盖的区域(扣图)。
cv2.bitwise_and(src1,src2[,mask])
-
src1
:第一个输入数组。通常是输入的原始图像。 -
src2
:第二个输入数组。它可以是另一个图像、一个常数值或者与src1
相同的图像。-
当应用掩膜时,这个参数经常就是
src1
本身;即对同一个图像进行操作。 -
如果对两个不同的图像执行按位与操作(例如,将两张图片的某些部分组合在一起),可以分别将它们作为
src1
和src2
输入到cv2.bitwise_and()
函数中,创建复杂的图像效果或进行图像合成。
-
-
mask
:掩膜(可选)。输入数组元素只有在该掩膜非零时才被处理。是一个8位单通道的数组,尺寸必须与src1
和src2
相同。 -
返回值:输出数组,应用掩膜后的图像,与输入数组大小和类型相同。
9.3颜色替换
前一个实验中,我们已经能够识别到图像中的某一种颜色,那么我们就可以对识别到的颜色进行一个操作,比如将其替换成别的颜色,其原理就是在得到原图的掩膜之后,对掩膜中的白色区域所对应的原图中的区域进行一个像素值的修改即可。
9.3.1制作掩膜
通过制作的掩膜,我们就可以对掩膜中的白色区域所对应的原图中的区域(也就是原图中的红色区域)进行像素值的修改,从而完成颜色替换的功能。
9.3.2颜色替换
由于掩膜与原图的大小相同,并且像素位置一一对应,那么我们就可以得到掩膜中白色(也就是像素值为255)区域的坐标,并将其带入到原图像中,即可得到原图中的红色区域的坐标,然后就可以修改像素值了,这样就完成了颜色的替换,如下图所示:
-
mask_image_np == 255
: 这一部分实际上是在生成一个布尔数组,其形状与mask_image_np
相同。 -
image_np[...] = (0, 255, 0)
: 这里使用了NumPy的高级索引功能。
img=cv.imread("images/demo.png") img_np=cv.resize(img,(400,400)) img1_np=img_np.copy() # 颜色空间转为HSV hsv_img_np=cv.cvtColor(img_np,cv.COLOR_BGR2HSV) # 创建掩膜:cv2.inRange(img,(h,s,v)min,(h,s,v)max) color_low=np.array([23,46,46]) color_hight=np.array([34,255,255]) # 创建掩膜:大小和原图一致,二值化图像 mask_yellow=cv.inRange(hsv_img_np,color_low,color_hight) # 与运算:把原图和掩膜与运算 color_img=cv.bitwise_and(img_np,img_np,mask=mask_yellow) # 替换颜色 img1_np[mask_yellow==255]=(0,255,0) cv.imshow("img_np",img_np) cv.imshow("img1_np",img1_np) cv.imshow("hsv",hsv_img_np) cv.imshow("mask",mask_yellow) cv.imshow("color",color_img) cv.waitKey(0) cv.destroyAllWindows()
10、ROI切割
ROI:Region of Interest,翻译过来就是感兴趣的区域。什么意思呢?比如对于一个人的照片,假如我们要检测眼睛,因为眼睛肯定在脸上,所以我们感兴趣的只有脸这部分,其他都不care,所以可以单独把脸截取出来,这样就可以大大节省计算量,提高运行速度。
还记得Numpy这个库吗?我们在使用OpenCV进行读取图像时,图像数据会被存储为Numpy数组,这也意味着我们可以使用Numpy数组的一些操作来对图像数据进行处理,比如切片。而本实验的原理也是基于Numpy数组的切片操作来完成的,因此在对应的组件中就需要填我们要切割的ROI区域的坐标来完成ROI切割操作。
在计算机视觉中,当我们使用OpenCV读取RGB三通道图像时,它会被转换成一个三维的Numpy数组。这个数组里的每个元素值都表示图像的一个像素值。这个三维数组的第一个维度(即轴0)通常代表图像的高度,第二个维度(即轴1)代表图像的宽度,而第三个维度(即轴2)代表图像的三个颜色通道(B、G、R,OpenCV读取到的图像以BGR的方式存储)所对应的像素值。
因此,我们可以通过指定切片的范围来选择特定的高度和宽度区域。这样,我们就能够获取这个区域内的所有像素值,即得到了这个区域的图像块,通过Numpy的切片操作,我们就完成了ROI切割的操作。这种提取ROI的方法允许我们仅获取感兴趣区域内的像素,而忽略其他不相关的部分,从而大大减少数据处理和存储的负担。
cat=cv.imread("images/cat.png") # (286,226) (360,304) (x,y) # 使用numpy切片实现ROI切割 roi=cat[226:304,286:360] roi[:]=(0,0,255) cv.imshow("roi",roi) cv.imshow("cat",cat) cv.waitKey(0) cv.destroyAllWindows()
11、图像添加水印
本实验中添加水印的概念其实可以理解为将一张图片中的某个物体或者图案提取出来,然后叠加到另一张图片上。具体的操作思想是通过将原始图片转换成灰度图,并进行二值化处理,去除背景部分,得到一个类似掩膜的图像。然后将这个二值化图像与另一张图片中要添加水印的区域进行“与”运算,使得目标物体的形状出现在要添加水印的区域。最后,将得到的目标物体图像与要添加水印的区域进行相加,就完成了添加水印的操作。这样可以实现将一个图像中的某个物体或图案叠加到另一个图像上,从而实现添加水印的效果。就本实验而言,会用到两个新的组件,一个是模板输入,一个是图像融合。
11.1模板输入
将模版进行灰度化,再进行二值化处理:
要得到模版:反阈值法
要得到背景:阈值法
11.2与运算
得到水印:模板与二值化图像与运算
得到背景:原图与二值化图像与运算
11.3图像融合(图像位与操作)
将模板的形状添加到水印区域之后,就可以将该图像与原始的模板图进行图像融合。该组件的目的就是将图像对应的数组中的对应元素进行相加(一定要注意这里的两个数组是规格相同的,也就是说要么都是灰度图,要么都是彩图),其过程如下图所示。
- 直接相加
- cv.daa(img1,img2)
bg=cv.imread("images/bg.png") logo=cv.imread("images/logo.png") # 获取logo大小 h,w=logo.shape[:2] # 从背景中切割出和logo一样的大小的子区域 roi=bg[:h,:w] # 将logo转灰度 gray_logo=cv.cvtColor(logo,cv.COLOR_BGR2GRAY) # 创建掩膜:白色logo,目的是获取到logo的颜色,有logo没背景 _,white=cv.threshold(gray_logo,170,255,cv.THRESH_BINARY_INV) #反阈值法 fg1=cv.bitwise_and(logo,logo,mask=white) # 创建掩膜:黑色的logo,目的是获取到背景颜色,有背景没logo _,black=cv.threshold(gray_logo,170,255,cv.THRESH_BINARY)#阈值法 fg2=cv.bitwise_and(roi,roi,mask=black) # 图像融合 roi[:]=cv.add(fg1,fg2) roi[:]=fg1+fg2 # 让融合后的图像赋值给切割出来的区域,这样原图中也会被修改像素值 cv.imshow("bg",bg) cv.imshow("white",white) cv.imshow("fg1",fg1) cv.imshow("roi",roi) cv.imshow("fg2",fg2) cv.waitKey(0) cv.destroyAllWindows()
12、图像噪点消除
噪声:指图像中的一些干扰因素,通常是由图像采集设备、传输信道等因素造成的,表现为图像中随机的亮度,也可以理解为有那么一些点的像素值与周围的像素值格格不入。常见的噪声类型包括高斯噪声和椒盐噪声。高斯噪声是一种分布符合正态分布的噪声,会使图像变得模糊或有噪点。椒盐噪声则是一些黑白色的像素值分布在原图像中。
滤波器:也可以叫做卷积核,与自适应二值化中的核一样,本身是一个小的区域,有着特定的核值,并且工作原理也是在原图上进行滑动并计算中心像素点的像素值。滤波器可分为线性滤波和非线性滤波,线性滤波对邻域中的像素进行线性运算,如在核的范围内进行加权求和,常见的线性滤波器有均值滤波、高斯滤波等。非线性滤波则是利用原始图像与模板之间的一种逻辑关系得到结果,常见的非线性滤波器中有中值滤波器、双边滤波器等。
注意:椒盐噪声可以理解为斑点,随机出现在图像中的黑点或白点;高斯噪声可以理解为拍摄图片时由于光照等原因造成的噪声。
12.1均值滤波
cv.blur(img,ksize,borderType(边缘填充))
均值滤波是一种最简单的滤波处理,它取的是卷积核区域内元素的均值,如3×3的卷积核:
对于边界的像素点,则会进行边界填充,以确保卷积核的中心能够对准边界的像素点进行滤波操作。在OpenCV中,默认的是使用BORDER_REFLECT_101的方式进行填充,下面的滤波方法中除了中值滤波使用的是BORDER_REPLICATE进行填充之外,其他默认也是使用这个方式进行填充
img=cv.imread("images/lvbo2.png") img=cv.resize(img,(480,480)) img2=cv.imread("images/lvbo3.png") img2=cv.resize(img2,(480,480)) # 均值滤波 img_blur=cv.blur(img,(3,3),borderType=cv.BORDER_REFLECT) cv.imshow("img",img) cv.imshow("blur",img_blur) cv.waitKey(0) cv.destroyAllWindows()
12.2方框滤波
cv.boxFilter(img,ddepth,ksize,normalize=True)
参数:
-
ksize:代表卷积核的大小,eg:ksize=3,则代表使用3×3的卷积核。
-
ddepth:输出图像的深度,-1代表使用原图像的深度。
-
图像深度是指在数字图像处理和计算机视觉领域中,每个像素点所使用的位数(bit depth),也就是用来表示图像中每一个像素点的颜色信息所需的二进制位数。图像深度决定了图像能够表达的颜色数量或灰度级。
-
-
normalize:当normalize为True的时候,方框滤波就是均值滤波,上式中的a就等于1/9;normalize为False的时候,a=1,相当于求区域内的像素和。
方框滤波跟均值滤波很像,如3×3的滤波核如下:
img=cv.imread("images/lvbo2.png") img=cv.resize(img,(480,480)) img2=cv.imread("images/lvbo3.png") img2=cv.resize(img2,(480,480)) # 方框滤波:cv2.boxFilter(img,ksize) img_box1=cv.boxFilter(img,-1,(3,3),normalize=True)#相当于均值滤波器 img_box2=cv.boxFilter(img,-1,(3,3),normalize=False)#把区域内的像素值全部相加 cv.imshow("img",img) cv.imshow("box1",img_box1) cv.imshow("box2",img_box2) cv.waitKey(0) cv.destroyAllWindows()
12.3高斯滤波
cv.GaussianBlur(img,ksize,sigmaX)
参数:
ksize:代表卷积核的大小,eg:ksize=3,则代表使用3×3的卷积核。
sigmaX:就是高斯函数里的值,σx值越大,模糊效果越明显。高斯滤波相比均值滤波效率要慢,但可以有效消除高斯噪声,能保留更多的图像细节,所以经常被称为最有用的滤波器。
-
高斯滤波是一种常用的图像处理技术,主要用于平滑图像、去除噪声。它通过使用高斯函数(正态分布)作为卷积核来对图像进行模糊处理。
前面两种滤波方式,卷积核内的每个值都一样,也就是说图像区域中每个像素的权重也就一样。高斯滤波的卷积核权重并不相同:中间像素点权重最高,越远离中心的像素权重越小。
img=cv.imread("images/lvbo2.png") img=cv.resize(img,(480,480)) img2=cv.imread("images/lvbo3.png") img2=cv.resize(img2,(480,480)) # 高斯滤波:cv2.GaussianBlur(img,ksize,sigmaX) img_gauss=cv.GaussianBlur(img,(3,3),1) cv.imshow("img",img) cv.imshow("gauss",img_gauss) cv.waitKey(0) cv.destroyAllWindows()
12.4中值滤波
cv2.medianBlur(img,ksize)
中值又叫中位数,是所有数排序后取中间的值。中值滤波没有核值,而是在原图中从左上角开始,将卷积核区域内的像素值进行排序,并选取中值作为卷积核的中点的像素值
中值滤波就是用区域内的中值来代替本像素值,所以那种孤立的斑点,如0或255很容易消除掉,适用于去除椒盐噪声和斑点噪声。中值是一种非线性操作,效率相比前面几种线性滤波要慢。
img=cv.imread("images/lvbo2.png") img=cv.resize(img,(480,480)) img2=cv.imread("images/lvbo3.png") img2=cv.resize(img2,(480,480)) # 中值滤波:cv2.medianBlur(img,ksize) img_median=cv.medianBlur(img2,3) cv.imshow("img",img) cv.imshow("median",img_median) cv.waitKey(0) cv.destroyAllWindows()
12.5双边滤波
cv2.bilateralFilter(img,d,sigmaColor,sigmaSpace)
参数:
-
ksize:卷积核的大小
-
d:过滤时周围每个像素领域的直径,这里已经设置了核大小。d=9===>9x9
-
sigmaColor:在color space(色彩空间)中过滤sigma。参数越大,那些颜色足够相近的的颜色的影响越大。较大的
sigmaColor
值意味着更大的颜色差异将被允许参与到加权平均中,从而使得颜色相近但不完全相同的像素也能够相互影响。 -
sigmaSpace:在coordinate space(坐标空间)中过滤sigma。这个参数是坐标空间中的标准差,决定了像素位置对滤波结果的影响程度。它定义了在图像的空间域中,一个像素可以影响周围像素的最大距离。换句话说,它控制着滤波器作用的范围大小。
双边滤波明显保留了更多边缘信息。双边滤波的基本思路是同时考虑将要被滤波的像素点的空域信息(周围像素点的位置的权重)和值域信息(周围像素点的像素值的权重)。为什么要添加值域信息呢?是因为假设图像在空间中是缓慢变化的话,那么临近的像素点会更相近,但是这个假设在图像的边缘处会不成立,因为图像的边缘处的像素点必不会相近。因此在边缘处如果只是使用空域信息来进行滤波的话,得到的结果必然是边缘被模糊了,这样我们就丢掉了边缘信息,因此添加了值域信息。
双边滤波采用了两个高斯滤波的结合,一个负责计算空间邻近度的权值(也就是空域信息),也就是上面的高斯滤波器,另一个负责计算像素值相似度的权值(也就是值域信息),也是一个高斯滤波器。
关于2个sigma参数:
简单起见,可以令2个sigma的值相等;
如果他们很小(小于10),那么滤波器几乎没有什么效果;
如果他们很大(大于150),那么滤波器的效果会很强,使图像显得非常卡通化。
关于参数d:
过大的滤波器(d>5)执行效率低。
对于实时应用,建议取d=5;
对于需要过滤严重噪声的离线应用,可取d=9;
img=cv.imread("images/lvbo2.png") img=cv.resize(img,(480,480)) img2=cv.imread("images/lvbo3.png") img2=cv.resize(img2,(480,480)) # 双边滤波:cv2.bilateralFilter(img,ksize,sigmaColor,sigmaSpace) img_bilateral=cv.bilateralFilter(img2,9,140,140) cv.imshow("img",img) cv.imshow("bilateral",img_bilateral) cv.waitKey(0) cv.destroyAllWindows()
12.6小结
在不知道用什么滤波器好的时候,优先高斯滤波,然后均值滤波。
斑点和椒盐噪声优先使用中值滤波。
要去除噪点的同时尽可能保留更多的边缘信息,使用双边滤波。
线性滤波方式:均值滤波、方框滤波、高斯滤波(速度相对快)。
非线性滤波方式:中值滤波、双边滤波(速度相对慢)。