直方图均衡
一、直方图概念
灰度图灰度分布:
- 横坐标为灰度值
- 纵坐标为对应灰度值的像素个数
cv2.calcHist(images,channels, mask, histSize, ranges)
- images : 原图像格式为 uint8 或 float32.当传入图像时,应使用中括号[]来括起
- channels:同样使用括号括起,表明了统计整幅图像的直方图通道;若传入图像是灰度图则它的值就是[0],若是彩色图像,则其值可为[0][1][2],分别对应着BGR
- mask:掩膜图像,统计整幅图像就将其设置为None,但是若想统计某一图像区域的直方图,便可定义一个掩膜图像并代入
- histSize:BIN的数目,也应用中括号括起,通常为 256,也就是灰度区间长度
- ranges:像素值范围,常为[0, 255]
import cv2
import matplotlib
import matplotlib.pyplot as plt #取别名(用于绘图展示)
import numpy as np #取别名,下面是notepad专用,立即显示图像
%matplotlib inline
def cv_show(name, img): #定义函数用于显示图片,此处name为窗口名,img为cv2调用imread方法的返回值
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def winds(images):
for i in range(len(images)):
images[i] = cv2.resize(images[i], (0, 0), fx = 0.8, fy = 0.8) # 注意此处的resize用法!
res = np.hstack(images)
cv_show('Compare', res)
# 转化为灰度图便于统计
img = cv2.imread('lena.png', 0)
cv_show('Lena', img)
# 统计
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
hist.shape
(256, 1)
plt.hist(img.ravel(),256)
plt.show()
# 确实是一张好图
img = cv2.imread('lena.png', -1)
color = ('b', 'g', 'r')
for i,col in enumerate(color): # enumerate 便于遍历下标
histr = cv2.calcHist([img], [i], None, [256], [0, 255]) # 此处应该传入彩色图
plt.plot(histr, color = col)
plt.xlim([0, 255])
二、mask操作
# 利用灰度图来演示
img = cv2.imread('lena.png', 0)
cv_show('lena',img)
img.shape
(512, 512)
# 利用切片创建mask
mask = np.zeros(img.shape[:2], np.uint8) # 利用shape切片返回img的尺寸元组,并借此创建全0阵列
mask[106:406, 106:406] = 255 # 切片不能使用二维数组,最好直接只用一个[]
cv_show("mask", mask)
使用 阵列与 操作
masked_img = cv2.bitwise_and(img, img, mask = mask)
cv_show('masked_img',masked_img)
hist_Origin = cv2.calcHist([img], [0], None, [256], [0, 255])
hist_Masked = cv2.calcHist([masked_img], [0], None, [256], [1, 256]) # 有一个很恐怖
plt.subplot(221),plt.imshow(img, 'gray') # 这里传入的221,就是 2 行 2 列 第一幅图的意思
plt.subplot(222),plt.imshow(mask, 'gray')
plt.subplot(223),plt.imshow(masked_img, 'gray')
plt.subplot(224),plt.plot(hist_Origin),plt.plot(hist_Masked),
# 蓝线为原始、橙红为ROI
plt.xlim([0, 255])
plt.show()
三、直方图均衡化
主要是为了增强图像的对比度
low = cv2.imread('low.jpg',0)
cv_show('Low', low)
# 操作来一遍
hist = cv2.calcHist([low], [0], None, [256], [0,255])
plt.plot(hist) # 点线图
plt.show()
plt.hist(low.ravel(), 256) # 面积图
plt.show()
观察上图可以发现,低对比图的灰度分布通常比较臃肿、集中,所以我们就要将其均匀化
- 均衡化流程如下:
# 对直方图进行均衡,但是如何从直方图中得出图像呢?
equ = cv2.equalizeHist(low) #此处的equ就是图像阵列
plt.hist(equ.ravel(), 256)
plt.show()
从上图可以看出,高对比度的图像灰度分布就不那么臃肿,更加均匀了
res = np.hstack([low, equ])
cv_show('Compare',res)
只能说效果显著:
## 再走一边流程
lena = cv2.imread('lena.png', 0)
lena.shape
(512, 512)
## 先绘制直方图
plt.hist(lena.ravel(),256) # 后面参数是灰度值变化范围
plt.show()
相对来说还是比较均匀的
# 绘制直方图均衡后的直方图
equ = cv2.equalizeHist(lena)
plt.hist(equ.ravel(), 256)
plt.show()
winds([lena, equ])
效果实有增强
四、自适应直方图均衡化
- 避免已有的ROI区域被其他无关区域“拖累”,可以采用局部直方图均衡
- 局部直方图可以避免ROI对比度被冲淡,但是可能会受到噪声影响
loc = cv2.imread('Local.png',0)
# 先创建局部的直方图均衡化的滤波器
clahe = cv2.createCLAHE(clipLimit = 2.0, tileGridSize=(8, 8))
res_clahe = clahe.apply(loc) # 运用滤波器
equ = cv2.equalizeHist(loc)
winds([loc,equ, res_clahe])
效果改善不少。相较B,C 图的面部细节更好
# 显示原图像直方图
plt.hist(loc.ravel(),256)
plt.title('Orgin')
plt.show()
plt.hist(equ.ravel(),256)
plt.title('Equ')
plt.show()
plt.hist(res_clahe.ravel(),256)
plt.title('Clahe')
plt.show()