1. 图像直方图
图像直方图是一种用于表示数字图像中亮度分布的方法,绘制了图像中每个亮度值对应的像素数。 在这个直方图中,横坐标左侧是较暗的区域,右侧是较亮的区域。 因此,较暗的图片的直方图中的数据大多集中在左侧和中间部分,而只有少量阴影的整体明亮图像则相反。
直方图是基于灰度图像绘制的,而不是基于彩色图像。 假设有一张信息图像(灰度值0~255,已知数的范围包含256个值,那么这个范围可以按照一定的规则划分子区域(即bins):
然后统计每个bin的像素数,可以得到下图(其中 x 轴代表 bin,y 轴代表每个 bin 中的像素数):
直方图是图像中像素强度分布的图形表示,计算了每个强度值具有的像素数。不同图像的直方图可能相同。
2. 掩膜
掩膜是利用选定的图像遮挡待处理的图像,以控制图像处理区域。在数字图像处理中,我们通常使用二维矩阵数组进行掩膜。 掩膜是由0和1组成的二值图像,1值覆盖的原图像被处理,0值不会被处理。
掩膜主要用于提取图像中的感兴趣区域,利用预先制作的感兴趣区域和待处理图像的掩码进行与运算,得到感兴趣区域图像。感兴趣区域内图像的值保持不变,区域外图像的值全为0。
cv.calcHist(images, channels, mask, histSize, ranges)
参数:
images 原图像
channels 如果输入图像是灰度图,它的值就是 0;如果是彩色图像的话,传入的参数可以是 0、1、2,分别对应通道 B、G、R。
mask 掩模图像
histSize bin 的数目
ranges 像素值范围,通常为 [0, 256]
当传入参数时应该用中括号 [] 括起来,例如:[img]。
例1:以灰度图方式读取图像,绘制图像的直方图。
import matplotlib
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
font = {
"family": "Microsoft YaHei"
}
matplotlib.rc("font", **font)
# 灰度模式加载图像
img = cv.imread("./image/cat.jpeg", 0)
# 直方图
hist = cv.calcHist([img], [0], None, [256], [0, 256])
plt.plot(hist)
plt.title("直方图")
plt.show()
# 创建掩膜
mask = np.zeros(img.shape[:2], np.uint8)
mask[400:650, 200:500] = 1
# 掩膜运算
mask_img = cv.bitwise_and(img, img, mask=mask)
plt.imshow(mask_img, cmap=plt.cm.gray)
plt.title("有掩膜的图像")
plt.show()
# 有掩膜的直方图
mask_hist = cv.calcHist([img], [0], mask, [256], [0, 256])
plt.clf()
plt.plot(mask_hist)
plt.title("有掩膜的直方图")
plt.show()
输出:
3. 直方图均衡化
直方图均衡化是一种对原始图像进行变换以获得具有均匀分布的灰度直方图的新图像的方法。 直方图均衡法的基本思想是展宽图像中像素个数多的灰度级,降低像素个数少的灰度级,从而达到图像清晰的目的。
假设有一个 64×64 像素和 8 个灰度级的图像。 每个灰度级的概率分布如下表所示。
问题解决步骤:
1)确定图像的灰度。如果我们的图像是彩色的,我们需要将其转换为灰度图像。 灰度等级一般为0~255。 这道题的灰度等级只有8级。
2)计算原始直方图的概率。计算原始图像上每个灰度像素占总数的比例,记为 Pi 。
3)计算直方图概率的累积值S(i) :
一直累加, 直到总和为1。
4)根据下面的公式得到像素映射关系。
这里的pix指的是灰度级,即(最大灰度级-最小灰度级)*累积概率+0.5,再取整数。
5)灰度映射。
找出原图与均衡后图像灰度的对应关系,将原图的每个像素点映射成一个新的像素点。至此图像直方图均衡化已经完成 。
cv.equalizeHist(img)
参数:
img 灰度图像
4. 自适应的直方图均衡化
上面的直方图均衡化考虑的是图像的全局对比度。 在直方图均衡化之后,图片背景的对比度发生了变化,我们会丢失很多信息。所以,很多情况下,这样的效果并不好。 如下图所示,对比下面两张图片中的雕像,我们因为太亮而丢失了很多信息。
自适应的直方图均衡化可以解决这个问题。整个图像会被分成很多小块,这些小块被称为tiles。OpenCV中默认的tiles大小是8x8。然后,对每个小块分别进行直方图均衡化。在每个区域中,直方图都会集中在一个小区域内,如果有噪音,噪音会被放大。为了避免这种情况,可以使用对比度限制。对于每个小块,如果直方图中的 bin 超过对比度的上限,则其中的像素会均匀地分散到其他 bin 中,然后对直方图进行均衡化。
最后,为了去除每个小块之间的边界,使用双线性差值对每个小块进行拼接。
cv.createCLAHE(clipLimit, tileGridSize)
参数:
clipLimit: 对比度限制,默认为 40。
tileGridSize 分块的大小,默认为 8∗8。
返回值:
CLAHE对象。
然后,调用返回值的apply(img)方法对img图像进行自适应的直方图均衡化操作。
例2:对上面的图像进行直方图均衡化和自适应的直方图均衡化。
import matplotlib
import cv2 as cv
import matplotlib.pyplot as plt
font = {
"family": "Microsoft YaHei"
}
matplotlib.rc("font", **font)
# 灰度模式加载图像
img = cv.imread("./image/cat.jpeg", 0)
# 图像均衡化
eq_hist_res = cv.equalizeHist(img)
plt.imshow(eq_hist_res, "Greys")
plt.title("均衡化的图像")
plt.show()
# 绘制均衡化的直方图
eq_hist = cv.calcHist([eq_hist_res], [0], None, [256], [0, 256])
plt.plot(eq_hist)
plt.title("均衡化的直方图")
plt.show()
# 图像自适应均衡化
cl = cv.createCLAHE()
clahe = cl.apply(img)
plt.imshow(clahe, cmap=plt.cm.gray)
plt.title("自适应均衡化的图像")
plt.show()
# 绘制均衡化的直方图
eq_hist_clahe = cv.calcHist([clahe], [0], None, [256], [0, 256])
plt.plot(eq_hist_clahe)
plt.title("自适应均衡化的直方图")
plt.show()
输出: