基于Python的OpenCV图像处理12

Jun 7,2017   2921 words   11 min


直方图均衡化

直方图均衡化简单来说就是使一幅图像的灰度分布尽可能地分布均匀,这样会改善图像的对比度。如果灰度分布集中在某一个小范围,那么这个图像 物体间的对比度会较差,同时,根据集中的位置,可以反映图像的整体明暗情况。首先通过下面的图片直观感受一下灰度直方图与 图像明暗的关系。 可以看到在第一幅图像中,整体偏暗,而且对比度很低。第二幅图像整体偏亮,对比度相较于第一幅影像要好。

均衡化原理

累计概率密度函数

首先了解直方图均衡化原理。首先给出累计概率密度函数公式:

\[s_{k}=\sum_{j=0}^{k}\frac{n_{j}}{n},k=0,1,2...L-1\]

在公式中,n是图像中像素总和,nk是当前k级别下的像素个数, nk除以n表示当前灰度级在整幅图像中出现的概率密度。sk表示从0到k级的累计概率密度。 L是图像中总灰度级数。 例如某一个图像灰度级数是256,总像素数是100,灰度为0的有10个,1的有15个,2的有25个。 那么k=2时的累计概率密度就等于s0+s1+s2,而s0=n0/n,s1=n1/n,s2=n2/n。所以最后可以 算得各个独立的概率密度分别为:0.1、0.15、0.25,累计概率密度为0.5。

均衡化思想与步骤

在了解了公式之后,再来看均衡化的思想。如上面所说,我们的目的就是把某个比较集中的灰度 区间变成全部范围内的均匀分布。它对图像进行的是非线性拉伸,使得在一定范围内的像素数量 大致相同。从原理上来说,均衡化有三个步骤,分别是:

  • 统计图像中每个灰度级出现的次数
  • 由公式(上述)累加计算归一化的直方图
  • 由累计概率密度计算每个像素新的像素值

从编程的角度,需要考虑更多细节问题,有如下步骤:

  • 遍历统计图像各灰度级出现的次数
  • 由各灰度级出现次数除以总像素数求得各灰度级出现的概率
  • 按灰度级从小到大对概率进行累加,得到每一灰度级对应的累积概率密度
  • 将每一级对应的累计概率密度与总灰度级数相乘,得到每一级对应的新灰度值
  • 对新灰度值取整

OpenCV实现

在OpenCV中有对应的均衡化函数,只需要一行代码即可实现。代码如下:

灰度图像
# coding=utf-8
import cv2
from matplotlib import pyplot as plt

img = cv2.imread("E:\\060701.jpg", 0)

# 直方图均衡化函数
equal_img = cv2.equalizeHist(img)

# 绘制灰度分布直方图
plt.hist(img.ravel(), 256, [0, 255])
plt.hist(equal_img.ravel(), 256, [0, 255])

cv2.imshow("img", img)
cv2.imshow("equal_img", equal_img)
plt.show()

运行结果如下: 可以看到,经过均衡化以后的图像对比度明显提高了。从直方图的对比也可以看出来,处理 过的直方图更加均衡、覆盖整个范围。下图是对一幅卫星影像进行均衡化后的结果,可以看到 均衡以后,地物的对比度明显提高了。

彩色图像

对于彩色的图片来说,直方图均衡化一般不能直接对R、G、B三个分量分别进行上述操作, 而要将RGB转换成HSV,然后对V分量进行直方图均衡化操作。如下图是对彩色图像RGB分量 分别进行均衡化后得到的结果。 可以发现很多地方颜色改变了。这也就是为什么不能分别进行均衡化再合成。而产生这种情况的原因也很好解释。 因为每一个像素点的分量值是不相同的,所以会导致不同的拉伸结果。对应上述公式中的累计概率不同。 所以需要使用HSV色彩空间。这在之前已经说过了。V表示图像的亮度值,取值为0-255。所以我们要对它进行均衡化。 代码如下:

# coding=utf-8
import cv2

img = cv2.imread("E:\\060702.jpg")

# 将img转成HSV
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 分离通道
img_h = img_hsv[:, :, 0]
img_s = img_hsv[:, :, 1]
img_v = img_hsv[:, :, 2]

# 对V通道进行均衡化
equal_img_v = cv2.equalizeHist(img_v)

# 把均衡化后的V通道重新赋给HSV图像
img_hsv[:, :, 2] = equal_img_v

# 注意OpenCV的imshow只支持显示BGR图像,所以HSV图像显示之前需要重新转成BGR
equal_img = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR)

cv2.imshow("img", img)
cv2.imshow("equal_img", equal_img)
cv2.waitKey(0)

结果如下: 可以发现,这样均衡化后的效果要比上面好很多,颜色没有明显的变化。

CLAHE均衡化

CLAHE的英文全称是Contrast Limited Adaptive Histogram Equalization,中文名为 限制对比度自适应直方图均衡化。为什么会提出这个方法呢?首先来看个例子。下图是 一幅影像以及对其进行直方图均衡化后的对比图。 可以发现均衡化后的图像明亮的部分出现了“过曝”,丢失了一些细节。这是由于整幅图像 暗部较多,所以在均衡化的时候很容易把原本明亮的地方变得更亮。所以我们需要对全局 均衡化的方法提出改进。CLAHE均衡化就出现了。
首先我们需要使用自适应直方图均衡化。这种情况下,图像 会被分成很多小块,这些小块被称为“tiles”。在OpenCV中 tiles的大小默认为8×8。然后我们再分别对每一小块进行 直方图均衡化。所以在每一个的区域中, 直方图会集中在某一个小的区域中(除非有噪声干扰)。 如果有噪声的话,噪声会被放大。 为了避免这种情况的出现要使用对比度限制。 对于每个小块来说,如果直方图中的 bin 超过对比度的上限的话, 就把其中的像素点均匀分散到其他 bins 中, 然后在进行直方图均衡化。最后,为了去除每一个小块之间“人造的”(由于算法造成)边界, 再使用双线性差值,对小块进行缝合。 示例代码如下:

# coding=utf-8
import cv2
from matplotlib import pyplot as plt

img = cv2.imread("E:\\histogram.png", 0)

# 新建CLAHE对象
# 有clipLimit、tileGridSize两个可选参数
clahe = cv2.createCLAHE()
# apply()函数用于对图像进行CLAHE均衡化
equal_img = clahe.apply(img)

# 绘制灰度分布直方图
plt.hist(img.ravel(), 256, [0, 255])
plt.hist(equal_img.ravel(), 256, [0, 255])

cv2.imshow("image", img)
cv2.imshow("equal_image", equal_img)
plt.show()

效果如下,可以看到明亮部分的细节得以很好的保留, 而且均衡化后的直方图分布更均匀了。

本文作者原创,未经许可不得转载,谢谢配合

返回顶部