直方图均衡化opencv和手动实现

#引入
在图像质量较差的时候,我们一般可以通过对亮度和增益的控制来改善图像的显示,如何自动选取他们的最佳值?一种方式是寻找图像中最亮和最暗的像素值,将他们映射到纯白和纯黑,另一种方式是寻找图像中像素平均值作为中间灰度值,然后扩展范围以达到尽量充满可显示的值。

如何可视化图像的亮度值集和?答案是绘制图片各个通道的直方图和亮度直方图,通过这些分布,我们能得到一些有意义的统计量(例如最大值、最小值和平均亮度值)。如果我们能使图像中暗的部分变亮、亮的部分变暗,那么图像显示出来的质量就得到了改善,这个操作放在直方图里就是改变前面提到的统计量,如何对其进行改变使其能应用到整个图片的动态范围内?常用的方法就是对图像进行直方图均衡化。

#原理
直方图均衡化,即寻找一个映射函数,经过映射后直方图是平坦的。就是将图像的直方图从左下图变成右下图的样子:
在这里插入图片描述
从图片中也可以明显看出,将直方图从只聚集在中间灰度的形式变成分布在整个动态范围内的形式,就对应着图片上的暗的地方变亮,亮的地方变暗,由此图片的质量也就得到了提高。需要注意的是,右下图并不是直接由左下图变换得到的,正确的变换顺序是:对左下图进行映射,得到一个灰度级映射,原图使用这个灰度级映射得到右上图(均衡化之后的图)再绘制右上图的直方图,得到右下图。
如何得到这个灰度级映射?
首先计算累积分布直方图,就是将灰度直方图,从小灰度开始累加,后一个灰度对应的值是前面所有灰度对应的值的和,这样分布里面最大的值也就等于图片的总像素数量。
然后将累积直方图进行归一化处理,以便映射到期望的灰度范围。
再使用归一化的累积直方图来创建一个灰度级映射函数。对于每个输入灰度级,将其映射到新的输出灰度级上。
原图根据上述步骤得到的灰度级映射函数,将原始图像中的每个像素的灰度级替换为对应的映射后的灰度级。这样就完成了图像的灰度直方图均衡化。

#代码实现
图像灰度处理

img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

计算灰度直方图,由于使用的是字典进行存储,存储顺序并不是灰度顺序,因此计算灰度直方图之后需要对其进行排序才能计算累积直方图

h,w = img.shape
    dist = {}
    for i in range(h):
        for j in range(w):
            if img[i,j] not in dist.keys():
                dist[img[i,j]] = 1
            else:
                dist[img[i,j]] += 1
    l = sorted(dist.items())

计算累积直方图,在计算的过程中可直接对其进行归一化,及当前灰度对应的值除以总像素数再加前一个灰度的累积值。这样可以将这个值限制到0-1之间。此步骤还可以直接完成灰度级映射,通过将归一化累积直方图的值乘以最大灰度级别(通常是 255)并四舍五入得到

dist2 = {}
    sum=0
    n = h*w
    for key in l:
        sum = sum+key[1]/n
        dist2[key[0]] = np.round(sum*255)

应用灰度映射

hist_img = np.zeros((h,w)).astype(dtype=np.float32)
    for i in range(h):
        for j in range(w):
            hist_img[i,j] = dist2[img[i,j]]

此时已经得到了映射后的图片,即hist_img,我们可以打印出在这个过程中的灰度直方图和灰度级映射(dist和dist2)进行观察:
在这里插入图片描述
从图片中可以更直观的了解到这个变换的过程。

#完整代码

def Hist1(img):
    #1.灰度化处理
    img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #2.计算灰度直方图
    h,w = img.shape
    dist = {}
    for i in range(h):
        for j in range(w):
            if img[i,j] not in dist.keys():
                dist[img[i,j]] = 1
            else:
                dist[img[i,j]] += 1
    l = sorted(dist.items())
    #3.计算累计直方图并且进行归一化以及灰度级映射
    dist2 = {}
    sum=0
    n = h*w
    for key in l:
        sum = sum+key[1]/n
        dist2[key[0]] = np.round(sum*255)

    #4.应用灰度级映射
    hist_img = np.zeros((h,w)).astype(dtype=np.float32)
    for i in range(h):
        for j in range(w):
            hist_img[i,j] = dist2[img[i,j]]
    hist = cv2.calcHist([hist_img],[0],None,[256],[0,256])
    #画图
    plt.figure(figsize=(10,10))
    plt.subplot(221),plt.title("orignal image")
    plt.imshow(img,cmap="gray")
    plt.subplot(222),plt.title("equlizehist image")
    plt.imshow(hist_img,cmap="gray")
    plt.subplot(223),plt.bar(dist.keys(),dist.values()),plt.title("orignal histogram")
    plt.subplot(224),plt.hist(hist_img.ravel(),256),plt.title("hist histogram")
    plt.show()

img = cv2.imread("./photo.jpg")
Hist1(img)

这里是调用opencv的函数实现的,结果是一样的:

def Hist(img): #调用函数
    img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    hist = cv2.calcHist([img],[0],None,[256],[0,256]) 
    hist_img = cv2.equalizeHist(img)

    plt.figure(figsize=(10,10))
    plt.subplot(221),plt.title("orignal image")
    plt.imshow(img,cmap="gray")
    plt.subplot(222),plt.title("equlizehist image")
    plt.imshow(hist_img,cmap="gray")
    plt.subplot(223),plt.hist(img.ravel(),256),plt.title("orignal histogram")
    plt.subplot(224),plt.hist(hist_img.ravel(),256),plt.title("hist histogram")
    plt.show()
img = cv2.imread("./photo.jpg")
Hist(img)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值