灰度直方图是图像灰度级的函数,用来描述每个灰度级在图像矩阵中的像素个数或者占有率。图像的对比度是通过灰度级范围来度量的,而灰度级范围可通过观察灰度直方图得到,灰度级范围越大代表对比度越高;反之对比度越低,低对比度的图像在视觉上给人的感觉是看起来不够清晰,所以通过算法调整图像的灰度值,从而调整图像的对比度是有必要的。
#获得图像的灰度直方图
def calGrayHist(img):
h,w = img.shape[:2]
gray_hist = np.zeros([256], np.uint64)
for i in range(h):
for j in range(w):
gray_hist[img[i][j]] += 1
return gray_hist
#线性变换图像增强
def linearTrans(a, b, img):
assert isinstance(a, float) # 必须是浮点型,否则(np.uint8)会发生取余数操作
output_img = a * img + b
output_img[output_img > 255] = 255
output_img = np.around(output_img)
output_img = output_img.astype(np.uint8)
return output_img
output_img = linearTrans(2.0, 10, img)
以上线性变换是对整个灰度级范围使用了相同的参数,有的时候也需要针对不同灰度级范围进行不同的线性变换,这就是常用的分段线性变换,经常用于降低较亮或较暗区域的对比度来增强灰度级处于中间范围的对比度,或者压低中间灰度级处的对比度来增强较亮或者较暗区域的对比度。
线性变换图像增强的缺点:需要根据不同的应用及图像自身的信息进行合理的选择,可能需要进行多次测试,所以选择合适的参数是相当麻烦的。直方图正规化就是基于当前图像情况自动选取a和b的值的方法。
#直方图的正规化
#计算图像灰度级的最值
print("hist normal:")
def hist_normal(omin, omax, img):
Imin = np.min(img)
Imax = np.max(img)
a = float(omax-omin)/float(Imax-Imin)
b = omin-a * Imin
output_img = a * img + b
output_img = np.around(output_img)
output_img = output_img.astype(np.uint8)
return output_img
output_img = hist_normal(0, 255, img)
假设输入图像为I,宽为W,高为H,首先将其灰度值归一化到[0,1]范围,对于8位图来说,除以255即可。I(r,c)代表归一化后的第r行第c列的灰度值,输出图像记为O,伽马变换就是O(r, c) = I(r,c)**gamma。当gamma = 1时,图像不变。如果图像整体或者感兴趣区域较暗,则令0
直方图均衡化通常用来增加图像的局部对比度。对于背景和前景都太亮或者太暗的图像非常有用尤其是可以带来X光图像中更清晰的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。
1)获得图像对应的灰度图像,计算灰度图像的灰度直方图
2)计算灰度直方图的累加直方图
3)输入灰度级和输出灰度级之间的映射关系
4)根据映射关系循环输出图像的每一个像素的灰度级
#全局直方图均衡化
print("equalizeHist:")
#求取直方图均衡化的每个像素的映射关系:
def equalizeHist_map(img_gray):#输入必须是灰度图像
h, w = img_gray.shape
#计算灰度直方图
gray_hist = calGrayHist(img_gray)
#计算累加灰度直方图
cumulative_hist = np.zeros([256], np.uint64)
for p in range(256):
if p == 0:
cumulative_hist[p] = gray_hist[p]
else:
cumulative_hist[p] = cumulative_hist[p-1] + gray_hist[p]
#根据累加灰度直方图得到输入灰度级和输出灰度级之间的映射关系
relative_map = np.zeros([256], np.uint8)
coff = 256.0/(h*w)
for p in range(256):
q = coff * float(cumulative_hist[p]) -1
if q < 0:
relative_map[p] = 0
else:
relative_map[p] = math.floor(q)
return relative_map
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
relative_map = equalizeHist_map(img_gray)
img_result = np.zeros(img.shape, np.uint8)
h,w = img.shape[:2]
for i in range(h):
for j in range(w):
img_result[i][j] = relative_map[img[i][j]]
虽然全局直方图均衡化方法对提高对比度很有效,但是均衡化处理以后暗区域的噪声可能会被放大,变得清晰可见,而亮区域可能会损失信息。为了解决该问题,提出了自适应直方图均衡化(Aptive Histogram Equalization)方法
自适应直方图均衡化首先将图像划分为不重叠的区域块,然后对每一个块分别进行直方图均衡化。在没有噪声影响的情况下,每一个小区域的灰度直方图会被限制在一个小的灰度级范围内;但是如果有噪声,每一个分割的区域块执行直方图均衡化后,噪声会被放大。为了避免出现噪声这种情况,提出了“限制对比度”(Contrast Limiting),如果直方图的bin超过了提前预设好的“限制对比度”,那么会被裁剪,然后将裁剪的部分均匀分布到其他的bin,这样就重构了直方图.代码见pyhton opencv createCLAHE函数。