week4-Histogram Equalization数字图像处理(DIP)

直方图均衡化(Histogram Equalization)是一种增强图像对比度(Image Contrast)的方法,其主要思想是将一副图像的直方图分布变成近似均匀分布,从而增强图像的对比度。直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同。在对图像做进一步处理之前,直方图均衡化通常是对图像灰度值进行归一化的一个非常好的方法,并且可以增强图像的对比度。
直方图均衡化虽然只是数字图像处理(Digital Image Processing)里面的基本方法,但是其作用很强大,是一种很经典的算法。
均匀分布

在概率论和统计学中,均匀分布也叫矩形分布,它是对称概率分布,在相同长度间隔的分布概率是等可能的。 均匀分布由两个参数a和b定义,它们是数轴上的最小值和最大值,通常缩写为U(a,b)。

在这种情况下,直方图均衡化的变换函数是图像中像素值的累积分布函数(cumulative distribution function,简写为 cdf,将像素值的范围映射到目标范围的归一化操作)

  1. 直方图均衡化与对比度增强
  2. 直方图均衡化(HE)原理
  3. 自适应直方图均衡化(AHE)原理
  4. 限制对比度自适应直方图均衡化(CLAHE)原理和实现
  5. 自适应局部区域伸展(Local Region Stretch)直方图均衡化原理和实现

直方图均衡化与对比度增强

图 1 是一张皮卡丘的图片(图片来自:baidu),图片是一张959 * 959的彩图。可以看出很多线条都是雾蒙蒙的看不清楚,整张图片偏暗,并且皮卡丘与背景(其他小精灵)区别不是很明显。将其直方图绘制出来之后得到图 2,可以看出其灰度绝大多数分布在0-50之间,而直方图均衡化要做的就是让直方图尽可能地均匀分布在0~255内。
week4-Histogram Equalization数字图像处理(DIP)_第1张图片
图一:五彩斑斓的暗黑皮卡丘
week4-Histogram Equalization数字图像处理(DIP)_第2张图片
图二:图一对应的直方图

from PIL import Image
from pylab import *
#绘制直方图的代码
pil_im1 = Image.open(r'D:\123456\lrs_eq_img.jpg')
im = array(pil_im1.convert('L'))
figure()
hist(im.flatten(), 128)  # 对数据进行压平处理
show()

或者

def draw_histogram_(self, hists):
    ### draw a bar picture of the given histogram
    plt.figure()
    plt.bar(range(len(hists)), hists)
    plt.show()

下面直接使用Python库PIL中的ImageOps来完成直方图均衡化的过程,来看看结果图三如何。代码简单,一行即可:

eq_img = ImageOps.equalize(img)

week4-Histogram Equalization数字图像处理(DIP)_第3张图片
图三:原图用库函数进行均衡化

week4-Histogram Equalization数字图像处理(DIP)_第4张图片
图四:图三的直方图分布(可见强行凭空捏造无中生有了很多数据,然后强行均衡化)
week4-Histogram Equalization数字图像处理(DIP)_第5张图片
图五:均匀分布标准图(所有均衡化都是使直方图向这个图拟合的过程)

直方图均衡化(HE)原理

效果图

效果图在上面用作对比了。

实现代码

def histogram_equalization(self, img_arr, level = 256, **args):
     # calculate hists
    hists = self.calc_histogram_(img_arr, level)
    
    # equalization
    (m, n) = img_arr.shape
    hists_cdf = self.calc_histogram_cdf_(hists, m, n, level)  # calculate CDF
    
    arr = np.zeros_like(img_arr)
    arr = hists_cdf[img_arr]        # mapping
    
    return arr
    
def calc_histogram_cdf_(self, hists, block_m, block_n, level = 256):
    hists_cumsum = np.cumsum(np.array(hists))
    const_a = (level - 1) / (block_m * block_n)
    hists_cdf = (const_a * hists_cumsum).astype("uint8")
    return hists_cdf

自适应直方图均衡化(AHE)原理

效果图

week4-Histogram Equalization数字图像处理(DIP)_第6张图片
图六:

week4-Histogram Equalization数字图像处理(DIP)_第7张图片
图七:

实现代码

限制对比度自适应直方图均衡化(CLAHE)原理和实现

效果图

week4-Histogram Equalization数字图像处理(DIP)_第8张图片
图八:
week4-Histogram Equalization数字图像处理(DIP)_第9张图片
图九:

自适应局部区域伸展(Local Region Stretch)直方图均衡化原理和实现

效果图

week4-Histogram Equalization数字图像处理(DIP)_第10张图片
图十:线条颜色比原图更亮,而且彩色部分更加逼真。
week4-Histogram Equalization数字图像处理(DIP)_第11张图片
图十一:易见最后一张图的直方图是原图对均衡化的最优拟合

上面提到的AHE和CLAHE都是基于块状区域进行直方图均衡化的,但是能不能根据灰度级 区域 近似的区域进行均衡化呢?比如对图像中灰度级[min, max]范围里面的所有像素点进行均衡化,使得像素点的直方图尽量在[min, max]上均匀分布。在论文Adaptive Contrast Enhancement Using Local Region Stretching中,根据亮度(Brightness)对图像进行分割成几个区域,然后分别做直方图均衡化。下面根据论文的主要思想,引入下面的方法:统计图像直方图,按照灰度级划分为三个灰度区间,使得三个区间的像素点数量近似相等,这样就分别在[0, level1), [level1, level2), [level2, 255]三个灰度区间做直方图均衡化,最后合并。

原注释已经很清楚了,我就不必画蛇添足了。

def bright_wise_histequal(self, img_arr, level = 256, **args):
    ### split the image to three level accoding brightness, equalize histogram dividely
    ### @params img_arr : numpy.array uint8 type, 2-dim
    ### @params level : gray scale
    ### @return arr : the equalized image array
    def special_histogram(img_arr, min_v, max_v):
        ### calculate a special histogram with max, min value
        ### @params img_arr : 1-dim numpy.array
        ### @params min_v : min gray scale
        ### @params max_v : max gray scale
        ### @return hists : list type, length = max_v - min_v + 1
        hists = [0 for _ in range(max_v - min_v + 1)]
        for v in img_arr:
            hists[v - min_v] += 1
        return hists
    def special_histogram_cdf(hists, min_v, max_v):
        ### calculate a special histogram cdf with max, min value
        ### @params hists : list type
        ### @params min_v : min gray scale
        ### @params max_v : max gray scale
        ### @return hists_cdf : numpy.array
        hists_cumsum = np.cumsum(np.array(hists))
        hists_cdf = (max_v - min_v) / hists_cumsum[-1] * hists_cumsum + min_v
        hists_cdf = hists_cdf.astype("uint8")
        return hists_cdf
    def pseudo_variance(arr):
        ### caluculate a type of variance
        ### @params arr : 1-dim numpy.array
        arr_abs = np.abs(arr - np.mean(arr))
        return np.mean(arr_abs)
        
    # search two grayscale level, which can split the image into three parts having approximately same number of pixels
    (m, n) = img_arr.shape
    hists = self.calc_histogram_(img_arr)
    hists_arr = np.cumsum(np.array(hists))
    hists_ratio = hists_arr / hists_arr[-1]
    
    scale1 = None
    scale2 = None
    for i in range(len(hists_ratio)):
        if hists_ratio[i] >= 0.333 and scale1 == None:
            scale1 = i
        if hists_ratio[i] >= 0.667 and scale2 == None:
            scale2 = i
            break
    
    # split images
    dark_index = (img_arr <= scale1)
    mid_index = (img_arr > scale1) & (img_arr <= scale2)
    bright_index = (img_arr > scale2)
    
    # variance
    dark_variance = pseudo_variance(img_arr[dark_index])
    mid_variance = pseudo_variance(img_arr[mid_index])
    bright_variance = pseudo_variance(img_arr[bright_index])
    
    # build three level images
    dark_img_arr = np.zeros_like(img_arr)
    mid_img_arr = np.zeros_like(img_arr)
    bright_img_arr = np.zeros_like(img_arr)
    
    # histogram equalization individually
    dark_hists = special_histogram(img_arr[dark_index], 0, scale1)
    dark_cdf = special_histogram_cdf(dark_hists, 0, scale1)
    
    mid_hists = special_histogram(img_arr[mid_index], scale1, scale2)
    mid_cdf = special_histogram_cdf(mid_hists, scale1, scale2)
    
    bright_hists = special_histogram(img_arr[bright_index], scale2, level - 1)
    bright_cdf = special_histogram_cdf(bright_hists, scale2, level - 1)
    
    
    def plot_hists(arr):
        hists = [0 for i in range(256)]
        for a in arr:
            hists[a] += 1
        self.draw_histogram_(hists)
    
    # mapping
    dark_img_arr[dark_index] = dark_cdf[img_arr[dark_index]]
    mid_img_arr[mid_index] = mid_cdf[img_arr[mid_index] - scale1]
    bright_img_arr[bright_index] = bright_cdf[img_arr[bright_index] - scale2]
    
    # weighted sum
    #fractor = dark_variance + mid_variance + bright_variance
    #arr = (dark_variance * dark_img_arr + mid_variance * mid_img_arr + bright_variance * bright_img_arr)/fractor
    arr = dark_img_arr + mid_img_arr + bright_img_arr
    arr = arr.astype("uint8")
    return arr

代码参考

Github: lxcnju/histogram_equalization

你可能感兴趣的:(week4-Histogram Equalization数字图像处理(DIP))