一、灰度实验
将彩色图像转换为灰度图像的过程称为灰度化,这种做法在图像处理和计算机视觉领域非常常见。
灰度图与彩色图最大的不同就是:彩色图是由R、G、B三个通道组成,而灰度图只有一个通道,也称为单通道图像,所以彩色图转成灰度图的过程本质上就是将R、G、B三通道合并成一个通道的过程。本实验中一共介绍了三种合并方法,分别是最大值法、平均值法以及加权均值法。
1.最大值法
对于彩色图像的每个像素,它会从R、G、B三个通道的值中选出最大的一个,并将其作为灰度图像中对应位置的像素值。
基本思路:对读取的图像嵌套两层for循环取到每一个像素点,由于每个像素值为BGR三元组,对每个像素值调用max函数并按对应位置填入新单通道图片中。
实现代码:
h,w = img.shape[:2]
black = np.zeros((h,w), np.uint8)#建立全黑图片
#遍历行
for i in range(h):
#遍历列
for j in range(w) :
black[i,j]=max(cat[i,j,0], cat[i,j,1],cat[i,j,2])
2.均值法
思路同最大值法,不过在对像素值处理时,对于彩色图像的每个像素,它会将R、G、B三个通道的像素值全部加起来,然后再除以三,得到的平均值就是灰度图像中对应位置的像素值。
实现代码:
h,w = img.shape[:2]
black = np.zeros((h,w), np.uint8)
for i in range(h):
for j in range(w):
black[i,j] = np.uint8((int(cat[i,j,0]))+(int(cat[i,j,1]))+int((cat[i,j,2]))//3)
3.加权均值法
对于彩色图像的每个像素,它会按照一定的权重去乘以每个通道的像素值,并将其相加,得到最后的值就是灰度图像中对应位置的像素值。本实验中,权重的比例为: R乘以0.299,G乘以0.587,B乘以0.114,这是经过大量实验得到的一个权重比例,也是一个比较常用的权重比例。
所使用的权重之和应该等于1。这是为了确保生成的灰度图像素值保持在合理的亮度范围内,并且不会因为权重的比例不当导致整体过亮或过暗。
h,w = cat.shape[:2]
black = np.zeros((h,w), np.uint8)
#定义权重
wb,wg,wr = 0.114,0.587,0.299
#遍历行
for i in range(h):
for j in range(w) :
black[i,j]=round(wb*(cat[i,j,0]+wg*cat[i,j,1]+wr*cat[i,j,2]))
二、图像的二值化处理
一幅二值图像的二维矩阵仅由0、1两个值构成,“0”代表黑色,“1”代白色。由于每一像素(矩阵中每一元素)取值仅有0、1两种可能,所以计算机中二值图像的数据类型通常为1个二进制位。二值图像通常用于文字、线条图的扫描识别(OCR)和掩膜图像的存储。 其操作的图像也必须是灰度图。也就是说,二值化的过程,就是将一张灰度图上的像素根据某种规则修改为0和maxval(maxval表示最大值,一般为255,显示白色)两种像素值,使图像呈现黑白的效果,能够帮助我们更好地分析图像中的形状、边缘和轮廓等特征。
1.阈值法
阈值法就是通过设置一个阈值,将灰度图中的每一个像素值与该阈值进行比较,小于等于阈值的像素就被设置为0(通常代表背景),大于阈值的像素就被设置为maxval(通常代表前景)。对于我们的8位图像(0~255)来说,通常是设置为255。
实现代码:
#二值化,阈值法
thresh,binary = cv.threshold(img,thresh=127,maxval=255,cv.THRESH_BINARY)
2.反阈值法
跟阈值法相反,小于阈值的像素值设置为maxval,大于阈值的设为0
实现代码:
_,binary2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
3.截断阈值法
超过阈值的设置为阈值thresh,而小于阈值的像素不变。
实现代码:
_,trunc = cv.threshold(img,150,255,cv.THRESH_TRUNC)
4.低阈值零处理法
顾名思义就是将低于阈值的像素值设置为0,大于阈值的像素值不变。
实现代码:
_,zeros = cv.threshold(img,150,255,cv.THRESH_TOZERO)
5.超阈值零处理法
跟上一种方法相反
实现代码:
_,zeros_inv = cv.threshold(img,150,255,cv.THRESH_TOZERO_INV)
6.OTSU阈值法
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算法是通过一个值将这张图分前景色和背景色(也就是灰度图中小于这个值的是一类,大于这个值的是一类。例如,如果你设置阈值为128,则所有大于128的像素点可以被视作前景,而小于等于128的像素点则被视为背景。),通过统计学方法(最大类间方差)来验证该值的合理性,当根据该值进行分割时,使用最大类间方差计算得到的值最大时,该值就是二值化算法中所需要的阈值。通常该值是从灰度图中的最小值加1开始进行迭代计算,直到灰度图中的最大像素值减1,然后把得到的最大类间方差值进行比较,来得到二值化的阈值。
实现代码:
thresh,otsu = cv.threshold(img,127,255,cv.THRESH_OTSU)
7.自适应二值化
与二值化算法相比,自适应二值化更加适合用在明暗分布不均的图片,因为图片的明暗不均,导致图片上的每一小部分都要使用不同的阈值进行二值化处理,这时候传统的二值化算法就无法满足我们的需求了,于是就出现了自适应二值化。
自适应二值化方法会对图像中的所有像素点计算其各自的阈值,这样能够更好的保留图片里的一些信息。
实现代码:
#自适应二值法,必须结合阈值法或者反阈值法,blocksize,选取的小区域的面积也就是11*11的小块,c,算出的小区域阈值减去c值
binary3 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,blockSize=11,c=2)
三、掩膜
掩膜(Mask)是一种在图像处理中常见的操作,它用于选择性地遮挡图像的某些部分,以实现特定任务的目标。掩膜通常是一个二值化图像,并且与原图像的大小相同,其中目标区域被设置为1(或白色),而其他区域被设置为0(或黑色),并且目标区域可以根据HSV的颜色范围进行修改。
mask=cv.inRange(img,color_low,color_high)#制作掩膜
通过掩膜与原图的与运算,我们就可以提取出图像中被掩膜覆盖的区域(扣图)。
cv2.bitwise_and(src1,src2[,mask])
-
src1
:第一个输入数组。通常是输入的原始图像。 -
src2
:第二个输入数组。它可以是另一个图像、一个常数值或者与src1
相同的图像。-
当应用掩膜时,这个参数经常就是
src1
本身;即对同一个图像进行操作。 -
如果对两个不同的图像执行按位与操作(例如,将两张图片的某些部分组合在一起),可以分别将它们作为
src1
和src2
输入到cv2.bitwise_and()
函数中,创建复杂的图像效果或进行图像合成。
-
-
mask
:掩膜(可选)。输入数组元素只有在该掩膜非零时才被处理。是一个8位单通道的数组,尺寸必须与src1
和src2
相同。 -
返回值:输出数组,应用掩膜后的图像,与输入数组大小和类型相同。
由于掩膜与原图的大小相同,并且像素位置一一对应,那么我们就可以得到掩膜中白色(也就是像素值为255)区域的坐标,并将其带入到原图像中,即可得到原图中的选中区域的坐标,然后就可以修改像素值了,这样就完成了颜色的替换。
四、图像水印添加
添加水印的概念其实可以理解为将一张图片中的某个物体或者图案提取出来,然后叠加到另一张图片上。具体的操作思想是通过将原始图片转换成灰度图,并进行二值化处理,去除背景部分,得到一个类似掩膜的图像。然后将这个二值化图像与另一张图片中要添加水印的区域进行“与”运算,使得目标物体的形状出现在要添加水印的区域。最后,将得到的目标物体图像与要添加水印的区域进行相加,就完成了添加水印的操作。这样可以实现将一个图像中的某个物体或图案叠加到另一个图像上,从而实现添加水印的效果。
import cv2
import cv2 as cv
#读图
bg = cv.imread("../images/bg.png")
logo = cv.imread("../images/logohq.png")
#灰度化
gray = cv.cvtColor(logo, cv.COLOR_BGR2GRAY)
#创建掩膜
#白logo
_,mask1 = cv.threshold(gray,170,255,cv.THRESH_BINARY_INV)
cv2.imshow("mask1", mask1)
#黑logo
_,mask2 = cv.threshold(gray,170,255,cv.THRESH_BINARY)
cv2.imshow("mask2", mask2)
#从背景里面截取子区域
shape = logo.shape
roi = bg[:shape[0], :shape[1]]
#与运算
#拿logo
dst1 = cv.bitwise_and(logo, logo, mask=mask1)
cv.imshow("dst1", dst1)
#拿背景
dst2 = cv.bitwise_and(roi, roi, mask=mask2)
cv.imshow("dst2", dst2)
#图像融合
dst = cv.add(dst1, dst2)
cv.imshow("dst", dst)
roi[:]=dst
cv.imshow("roi", bg)
cv.waitKey(0)
cv.destroyAllWindows()
五、图像噪点消除
噪声:指图像中的一些干扰因素,通常是由图像采集设备、传输信道等因素造成的,表现为图像中随机的亮度,也可以理解为有那么一些点的像素值与周围的像素值格格不入。常见的噪声类型包括高斯噪声和椒盐噪声。高斯噪声是一种分布符合正态分布的噪声,会使图像变得模糊或有噪点。椒盐噪声则是一些黑白色的像素值分布在原图像中。
实现代码:
import cv2 as cv
#读图
lvbo2 = cv.imread("../images/lvbo2.png")
lvbo3 = cv.imread("../images/lvbo3.png")
#均值滤波
mean = cv.blur(lvbo2, (5, 5))
cv.imshow("mean", mean)
#方框滤波
box = cv.boxFilter(lvbo2, -1, (3, 3),normalize=False)
cv.imshow("box", box)
#高斯滤波
guss = cv.GaussianBlur(lvbo2, (3, 3), 1)
cv.imshow("guss", guss)
#中值滤波
middle = cv.medianBlur(lvbo3, 3)
cv.imshow("middle", middle)
#双边滤波,用了两个高斯核,同时考虑空域和值域
double = cv.bilateralFilter(lvbo2,9,150,150)
cv.imshow("double", double)
cv.waitKey(0)
cv.destroyAllWindows()
在不知道用什么滤波器好的时候,优先高斯滤波,然后均值滤波。
斑点和椒盐噪声优先使用中值滤波。
要去除噪点的同时尽可能保留更多的边缘信息,使用双边滤波。
线性滤波方式:均值滤波、方框滤波、高斯滤波(速度相对快)。
非线性滤波方式:中值滤波、双边滤波(速度相对慢)。