OpenCV(十九)直方图(直方图计算、掩膜、均衡化、自适应均衡化)
目录
一、基础理论
1、原理及作用
作用:
获取图像的强度(灰度)分布。
直方图是数据统计的一种方法,并且将统计值组织到一系列实现定义好的 bin当中。其中, bin为直方图中经常用到的一个概念,可以译为“直条”或“组距”,其数值是从数据中计算出的特征统计量,这些数据可以是诸如梯度、方向、色彩或任何其他特征。
图像直方图(Image Histogram)是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素个数。这种直方图中,横坐标的左侧为较暗的区域,而右侧为较亮的区域。因此—张较暗图片的直方图中的数据多集中于左侧和中间部分,而整体明亮、只有少量阴影的图像则相反。 (如图,左侧较暗,右侧较亮)灰度直方图是一幅图像中个像素灰度值出现次数或频数的统计结果,它只反映该图像中灰度值出现的频率,而未反映某一灰度值像素所在的位置。
直方图统计的是每一个灰度值所具有的像素个数。(不同图像直方图可能相同)
注意:直方图是根据灰度图进行绘制的,而不是彩色图像。
2、专业术语
BINS:上面的直方图显示每个像素值的像素数,即从0到255。(只需要16个值即可表示直方图,例如:找到介于0到15之间的像素数,然后找到16到31之间,…,240到255之间的像素数。)(因此,您要做的就是将整个直方图分成16个子部分)
DIMS:这是我们为其收集数据的参数的数量。在这种情况下,我们仅收集关于强度值的一件事的数据,所以这里是1。
RANGE:这是您要测量的强度值的范围。通常,它是[0,256]
,即所有强度值。
二、直方图计算
函数介绍:
注:这部分实现的是python代码
cv.calcHist(images,channels,mask,histSize,ranges [,hist [,accumulate]])
- images:它是uint8或float32类型的源图像。它应该放在方括号中,即“ [img]”。
- channels:也以方括号给出。它是我们计算直方图的通道的索引。例如,如果输入为灰度图像,则其值为[0]。对于彩色图像,您可以传递[0],[1]或[2]分别计算蓝色,绿色或红色通道的直方图。
- mask:图像掩码。为了找到完整图像的直方图,将其指定为“无”。但是,如果要查找图像特定区域的直方图,则必须为此创建一个掩码图像并将其作为掩码。(我将在后面显示一个示例。)
- histSize:这表示我们的BIN计数。需要放在方括号中。对于全尺寸,我们通过[256]。
- ranges:这是我们的RANGE。通常为[0,256]。
hist = cv2.calcHist([img], [0], None, [256], [0, 256]) # 图像 通道索引 掩码(完整/局部) bin计数 范围
1、灰度图
代码:
#计算直方图(灰度)
def Calculate_Hist():
# 1、读取图片, 并转换成灰度图
img = cv2.imread("Resource/test11.jpg", 0) # 0即灰度图,1彩图
cv2.imshow("img", img)
# 2、获取并绘制直方图
hist = cv2.calcHist([img], [0], None, [256], [0, 256]) #获取
# 图像 通道索引 掩码(完整/局部) bin计数 范围
plt.plot(hist) # 绘图(基于一点或多点)
# 3、直方图展示
plt.show() # 显示
效果:
打印直方图获取的像素(hist)结果:
前者较亮(直方图偏右),后者较暗(直方图偏左):
纯黑白:
2、彩色图
代码:
#计算直方图(彩图)
def Calculate_Hist_RGB():
# 1、读取图片
img = cv2.imread("Resource/test11.jpg") #彩图
cv2.imshow("img", img)
# 设置颜色通道
color = ["b", "g", "r"]
# 2、获取直方图
for i, c in enumerate(color): #i是索引,c是内容
hist = cv2.calcHist([img], [i], None, [256], [0, 256])
plt.plot(hist, color=c)
# 3、直方图展示
plt.legend(["B Channel", "G Channel", "R Channel"]) #批注
plt.show()
三、直方图掩膜的应用(mask)
1、基础理论
1、作用
(1)提取感兴趣区域:用预先制作的感兴趣区掩模与待处理图像进行"与“操作,得到感兴趣区图像,感兴趣区内图像值保持不变,而区外图像值都为0。
(2)屏蔽∶用掩模对图像上某些区域作屏蔽,使其不参加处理或不参加处理参数的计算,或仅对屏蔽区作处理或统计。
(3)结构特征提取∶用相似性变量或方图像匹配法检测和提取图像中与掩模相似的结构特征。·特殊形状图像制作。
掩膜在遥感影像处理中使用较多,当提取道路或者河流,或者房屋时,通过一个掩膜矩阵来对图像进行像素过滤,然后将我们需要的地物或者标志突出显示出来。(提取感兴趣区域)
2、原理
在数字图像处理中,通常使用二维矩阵数组进行掩膜。掩膜是由0和1组成一个二进制图像,利用该掩膜图像要处理的图像进行掩膜,其中1值的区域被处理,0值区域被屏蔽,不会处理。
2、代码
#掩膜
def Mask():
# 1、读取图片, 并转换成灰度图
img = cv2.imread("Resource/test11.jpg", 0)
cv2.imshow("img", img)
# 2、创建mask
mask = np.zeros(img.shape, np.uint8)
mask[100:400, 20:200] = 255 #白色掩膜
# 横坐标范围 纵坐标范围
cv2.imshow("mask", mask)
# 3、获取mask后的图像
masked_img = cv2.bitwise_and(img, img, mask=mask) #把掩膜用于图像
cv2.imshow("masked_img", masked_img)
# 4、获取并绘制直方图
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256]) #全部直方图
plt.plot(hist_full)
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256]) #掩膜直方图
plt.plot(hist_mask)
# 5、显示直方图
plt.show()
3、效果
四、直方图均衡化
1、基础理论
作用:通常用于提高图像的对比度。(强化细节)
想象一下,如果一副图像中的大多数像素点的像素值都集中在某一个小的灰度值值范围之内会怎样呢?如果一幅图像整体亮,那所有的像素值的取值个数应该都会很高。所以应该把它的直方图做一个横向拉伸(如下图),就可以扩大图像像素值的分布范围,提高图像的对比度,这就是直方图均衡化要做的事情。
图像均衡化:“直方图均衡化"是把原始图像的灰度直方图从比较集中的某个灰度区间变成在更广泛灰度范围内的分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。直方图被拉伸(均衡化)之后,细节自然能够强化。
函数:
cv2.equalizeHist(img) #直方图均衡化
2、代码
#直方图均衡化
def Equalize_Hist():
# 1、读取图片, 并转换成灰度图
img = cv2.imread("Resource/test11.jpg", 0) #灰度图
# 2、直方图均衡化
img_equal = cv2.equalizeHist(img) #直方图均衡化
# 3、显示直方图
f, ax = plt.subplots(2, 2, figsize=(16, 16))
#显示图像
ax[0, 0].imshow(img, "gray")
ax[0, 1].imshow(img_equal, "gray") #注:"gray"是有效名,不能乱写
#显示直方图
ax[1, 0].hist(img.ravel(), 256) #ravel():多维数组转一维数组
ax[1, 1].hist(img_equal.ravel(), 256)
#显示plt画板
plt.show()
3、效果
直方图均衡化的缺陷:由于太暗,损失了许多信息。 所以引入:自适应的直方图均衡化。
五、自适应的直方图均衡化
1、基础理论
直方图均衡化的缺陷:由于太暗,损失了许多信息。 所以引入:自适应的直方图均衡化。
作用:
在直方图均衡化的基础上,尽可能保留细节信息。(把直方图分成多个小块,防止细节信息由于过暗而损失掉)
为了解决这个问题,需要使用自适应的直方图均衡化。此时,整幅图像会被分成很多小块,这些小块被称为"tiles”(在OpenCV中tiles的大小默认是8x8),然后再对每一个小块分别进行直方图均衡化。所以在每一个的区域中,直方图会集中在某一个小的区域中)。如果有噪声的话,噪声会被放大。为了避免这种情况的出现要使用对比度限制。对于每个小块来说,如果直方图中的bin 超过对比度的上限的话,就把其中的像素点均匀分散到其他 bins 中,然后在进行直方图均衡化。
如下图,把直方图像素点分散:
为了去除每一个小块之间的边界,再使用双线性差值,对每个小块进行拼接。
函数:
cv.createCLAHE(clipLimit, titleGridSize)
参数1 clipLimit: 对比度限值(默认40)。(阈值)
参数2 titleGridSize: 分块的大小(默认8*8)。(分成几块)
2、代码
#自适应直方图均衡化
def Clahe_Hist():
# 1、读取图片, 并转换成灰度图
img = cv2.imread("Resource/test11.jpg", 0) #灰度图
# 2、自适应直方图均衡化
cl = cv2.createCLAHE(2.0, (8,8))
# 对比度限制 分块大小
clahe = cl.apply(img) #自适应直方图均衡化
# 3、显示直方图
f, ax = plt.subplots(2, 2, figsize=(16, 16))
#显示图像
ax[0, 0].set_title("origin")
ax[0, 0].imshow(img, "gray")
ax[0, 1].set_title("Equalized")
ax[0, 1].imshow(clahe, "gray") #注:"gray"是有效名,不能乱写
#显示直方图
ax[1, 0].hist(img.ravel(), 256) #ravel():多维数组转一维数组
ax[1, 1].hist(clahe.ravel(), 256)
#显示plt画板
plt.show()
3、效果
尤其是最后一张,可以明显感觉到普通均衡化和自适应均衡化的差距。
总代码
#直方图计算、掩膜及均衡化
import cv2
import numpy as np
from matplotlib import pyplot as plt
#plt:本库函数主要是一些plt常见的画图操作,如设置图的颜色,线条,字体,坐标轴,标题,legend,在画好的图上写文字等。
#计算直方图(灰度)
def Calculate_Hist():
# 1、读取图片, 并转换成灰度图
img = cv2.imread("Resource/test11.jpg", 0) # 0即灰度图,1彩图
cv2.imshow("img", img)
# 2、获取并绘制直方图
hist = cv2.calcHist([img], [0], None, [256], [0, 256]) #获取
# 图像 通道索引 掩码(完整/局部) bin计数 范围
plt.plot(hist) # 绘图(基于一点或多点)
# 3、直方图展示
plt.show() # 显示
#计算直方图(彩图)
def Calculate_Hist_RGB():
# 1、读取图片
img = cv2.imread("Resource/test11.jpg") #彩图
cv2.imshow("img", img)
# 设置颜色通道
color = ["b", "g", "r"]
# 2、获取直方图
for i, c in enumerate(color): #i是索引,c是内容
hist = cv2.calcHist([img], [i], None, [256], [0, 256])
plt.plot(hist, color=c)
# 3、直方图展示
plt.legend(["B Channel", "G Channel", "R Channel"]) #批注
plt.show()
#掩膜
def Mask():
# 1、读取图片, 并转换成灰度图
img = cv2.imread("Resource/test11.jpg", 0)
cv2.imshow("img", img)
# 2、创建mask
mask = np.zeros(img.shape, np.uint8)
mask[100:400, 20:200] = 255 #白色掩膜
# 高 宽
cv2.imshow("mask", mask)
# 3、获取mask后的图像
masked_img = cv2.bitwise_and(img, img, mask=mask) #把掩膜用于图像
cv2.imshow("masked_img", masked_img)
# 4、获取并绘制直方图
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256]) #全部直方图
plt.plot(hist_full)
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256]) #掩膜直方图
plt.plot(hist_mask)
# 5、显示直方图
plt.show()
#直方图均衡化
def Equalize_Hist():
# 1、读取图片, 并转换成灰度图
img = cv2.imread("Resource/test11.jpg", 0) #灰度图
# 2、直方图均衡化
img_equal = cv2.equalizeHist(img) #直方图均衡化
# 3、显示直方图
f, ax = plt.subplots(2, 2, figsize=(16, 16))
#显示图像
ax[0, 0].set_title("origin")
ax[0, 0].imshow(img, "gray")
ax[0, 1].set_title("Equalized")
ax[0, 1].imshow(img_equal, "gray") #注:"gray"是有效名,不能乱写
#显示直方图
ax[1, 0].hist(img.ravel(), 256) #ravel():多维数组转一维数组
ax[1, 1].hist(img_equal.ravel(), 256)
#显示plt画板
plt.show()
#自适应直方图均衡化
def Clahe_Hist():
# 1、读取图片, 并转换成灰度图
img = cv2.imread("Resource/vague1.jpg", 0) #灰度图
# 2、自适应直方图均衡化
cl = cv2.createCLAHE(2.0, (8,8))
# 对比度限制 分块大小
clahe = cl.apply(img) #自适应直方图均衡化
# 3、显示直方图
f, ax = plt.subplots(2, 2, figsize=(16, 16))
#显示图像
ax[0, 0].set_title("origin")
ax[0, 0].imshow(img, "gray")
ax[0, 1].set_title("Equalized")
ax[0, 1].imshow(clahe, "gray") #注:"gray"是有效名,不能乱写
#显示直方图
ax[1, 0].hist(img.ravel(), 256) #ravel():多维数组转一维数组
ax[1, 1].hist(clahe.ravel(), 256)
#显示plt画板
plt.show()
if __name__ == '__main__':
plt.style.use("fivethirtyeight") #直方图样式设计
plt.figure(figsize=(6, 4)) #直方图大小
Calculate_Hist() #计算直方图(灰度)
Calculate_Hist_RGB() #计算直方图(彩图)
Mask() #掩膜直方图
Equalize_Hist() #直方图均衡化
Clahe_Hist() #自适应直方图均衡化
参考资料
https://www.bilibili.com/video/BV1Fo4y1d7JL?p=29
更多推荐
所有评论(0)