OpenCV教程 (28)-- 直方图均衡化、直方图对比


 

目录

 

1. 直方图均衡化

1.1 目标

1.2 理论Theory

1.3 克拉赫(Contrast Limited 局部自适应直方图均衡化)

1.4 综合测试

2. 直方图比较


1. 直方图均衡化

 

1.1 目标

In this section,

在这一部分,

  • We will learn the concepts of histogram equalization and use it to improve the contrast of our images. 我们将学习直方图均衡化的概念,并使用它来提高我们的图像对比度。

1.2 理论Theory

Consider an image whose pixel values are confined to some specific range of values only. For eg, brighter image will have all pixels confined to high values. But a good image will have pixels from all regions of the image. So you need to stretch this histogram to either ends (as given in below image, from wikipedia) and that is what Histogram Equalization does (in simple words). This normally improves the contrast of the image.

假设一个图像的像素值仅限于某些特定的值范围。对于例如,更明亮的图像将有所有的像素限制在高值。但是一个好的图像应该包含图像所有区域的像素。所以你需要把这个直方图拉伸到两端(如下图所示,来自维基百科) ,这就是直方图均衡化的工作(简单来说)。这通常会提高图像的对比度。

 

I would recommend you to read the wikipedia page on Histogram Equalization for more details about it. It has a very good explanation with worked out examples, so that you would understand almost everything after reading that. Instead, here we will see its Numpy implementation. After that, we will see OpenCV function.

我建议你阅读维基百科上关于直方图均衡化的页面,了解更多细节。它有一个非常好的解释与工作出来的例子,所以你会理解几乎所有在阅读后。相反,这里我们将看到它的 Numpy 实现。之后,我们将看到 OpenCV 的功能。

def equalHist_demo(image):
    """
    直方图均衡化操作
    :param image: 输入为三通道彩色图像
    :return: 显示直方图均衡化结果
    """
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    dst = cv.equalizeHist(gray)
    res = np.hstack((gray, dst))  # stacking images side-by-side
    cv.imshow("stacking images side-by-side", res)

    # plot histogram equalization result
    hist, bins = np.histogram(gray.flatten( ), 256, [0, 256])
    cdf = hist.cumsum( )
    cdf_normalized = cdf * float(hist.max( )) / cdf.max( )
    cdf_m = np.ma.masked_equal(cdf, 0)
    cdf_m = (cdf_m - cdf_m.min( )) * 255 / (cdf_m.max( ) - cdf_m.min( ))
    # cdf = np.ma.filled(cdf_m, 0).astype('uint8')

    plt.plot(cdf_m, color = 'k')                # plot cdf mask
    plt.plot(cdf_normalized, color = 'b')       # plot normalized cdf

    plt.hist(gray.flatten( ), 256, [0, 256], color = 'r')   # src gray image hist
    plt.hist(dst.flatten( ), 256, [0, 256], color = 'y')    # dst equalized gray image hist

    plt.xlim([0, 256])
    plt.legend(('cdf_m', 'cdf_normalized','src histogram',"equalized hist"), loc = 'upper left')
    plt.show( )

彩色三通道图像->灰度图:

原图(左)灰度直方图均衡化前后对比:

灰度直方图统计:

So now you can take different images with different light conditions, equalize it and check the results.

所以现在你可以在不同的光线条件下拍摄不同的照片,平衡它并检查结果。

Histogram equalization is good when histogram of the image is confined to a particular region. It won't work good in places where there is large intensity variations where histogram covers a large region, ie both bright and dark pixels are present. Please check the SOF links in Additional Resources.

当图像的直方图局限于某个特定区域时,直方图均衡化效果很好。如果直方图覆盖的区域很大,比如有明亮和暗的像素,那么这种方法就不能很好地工作。请检查附加资源中的 SOF 链接。

 

1.3 克拉赫(Contrast Limited 局部自适应直方图均衡化)

CLAHE (Contrast Limited Adaptive Histogram Equalization)

The first histogram equalization we just saw, considers the global contrast of the image. In many cases, it is not a good idea. For example, below image shows an input image and its result after global histogram equalization.

我们刚刚看到的第一个直方图均衡化,考虑了图像的全球对比度。在许多情况下,这并不是一个好主意。例如,下面的图片显示了一个输入图片和它在全局直方图均衡化后的结果。

原图

全局灰度均衡结果

It is true that the background contrast has improved after histogram equalization. But compare the face of statue in both images. We lost most of the information there due to over-brightness. It is because its histogram is not confined to a particular region as we saw in previous cases (Try to plot histogram of input image, you will get more intuition).

的确,在2010年直方图均衡化之后,背景对比度有所改善。但是比较两张照片中雕像的脸。由于太亮了,我们失去了那里的大部分信息。这是因为它的直方图并不像我们之前看到的那样局限于特定的区域(尝试绘制输入图像的直方图,你会得到更多的直觉)。

So to solve this problem, adaptive histogram equalization is used. In this, image is divided into small blocks called "tiles" (tileSize is 8x8 by default in OpenCV). Then each of these blocks are histogram equalized as usual. So in a small area, histogram would confine to a small region (unless there is noise). If noise is there, it will be amplified. To avoid this, contrast limiting is applied. If any histogram bin is above the specified contrast limit (by default 40 in OpenCV), those pixels are clipped and distributed uniformly to other bins before applying histogram equalization. After equalization, to remove artifacts in tile borders, bilinear interpolation is applied.

所以为了解决这个问题,我们使用了适应直方图均衡化。在这里,图像被划分为称为“ tiles”的小块(在 OpenCV 中,tileSize 默认为8x8)。然后每个块像平常一样直方图均衡化。所以在一个很小的区域,直方图会局限于一个很小的区域(除非有噪声)。如果存在噪音,它就会被放大。为了避免这一点,应用了对比度限制。如果任何一个直方图 bin 高于指定的对比度限制(OpenCV 默认为40) ,这些像素会被剪切并均匀地分布到其他的箱子中,然后再应用直方图均衡化。在均衡化之后,为了移除边框中的工件,应用双线性插值。

下面的代码片段展示了如何在 OpenCV 中应用 CLAHE:

 

import numpy as np
import cv2 as cv
img = cv.imread('tsukuba_l.png',0)
# create a CLAHE object (Arguments are optional).
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
cv.imwrite('clahe_2.jpg',cl1)

See the result below and compare it with results above, especially the statue region:

请看下面的结果,并与上面的结果进行比较,尤其是雕像区域:

1.4 综合测试

#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@Copyright:  Jihua Lab 2021.
@File Name   :  1302-histogram applications.py
@Description:  


@Create Time   :   2021/1/8 0008  8:57
@Author :   HaoWANG, Foshan,China
@Email  :   haowanghk@163.com

@Software : Pycharm
@Version: V1.2

"""

import os
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt


def equalHist_demo(image):
    """
    直方图均衡化操作
    :param image: 输入为三通道彩色图像
    :return: 显示直方图均衡化结果
    """
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    dst = cv.equalizeHist(gray)
    res = np.hstack((gray, dst))  # stacking images side-by-side
    print("--------Histogram equalization and plot  --------")
    cv.imshow("stacking images side-by-side", res)
    cv.imshow("equalization result", dst)
    cv.imwrite("../pictures/equ_lena.png", dst)

    # plot histogram equalization result
    hist, bins = np.histogram(gray.flatten( ), 256, [0, 256])
    cdf = hist.cumsum( )
    cdf_normalized = cdf * float(hist.max( )) / cdf.max( )
    cdf_m = np.ma.masked_equal(cdf, 0)
    cdf_m = (cdf_m - cdf_m.min( )) * 255 / (cdf_m.max( ) - cdf_m.min( ))
    # cdf = np.ma.filled(cdf_m, 0).astype('uint8')

    plt.plot(cdf_m, color = 'k')  # plot cdf mask
    plt.plot(cdf_normalized, color = 'b')  # plot normalized cdf

    plt.hist(gray.flatten( ), 256, [0, 256], color = 'r')  # src gray image hist
    plt.hist(dst.flatten( ), 256, [0, 256], color = 'y')  # dst equalized gray image hist

    plt.xlim([0, 256])
    plt.legend(('cdf_m', 'cdf_normalized', 'src histogram', "equalized hist"), loc = 'upper left')
    plt.show( )


def clahe_demo(image):
    """
    克拉赫(Contrast Limited 适应直方图均衡化)
    :param image: 三通道彩色图像
    :return:绘制均衡化之后的结果图像
    """

    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)  # 彩色图像灰度化
    clahe = cv.createCLAHE(clipLimit = 1.0, tileGridSize = (8, 8))
    """
     @brief Creates a smart pointer to a cv::CLAHE class and initializes it.
    .   
    .   @param clipLimit Threshold for contrast limiting.
    .   @param tileGridSize Size of grid for histogram equalization. Input image will be divided into
    .   equally sized rectangular tiles. tileGridSize defines the number of tiles in row and column.
    """
    dst = clahe.apply(gray)
    print("--------Histogram equalization clahe_demo --------")

    # plot histogram equalization result
    hist, bins = np.histogram(gray.flatten( ), 256, [0, 256])
    cdf = hist.cumsum( )
    cdf_normalized = cdf * float(hist.max( )) / cdf.max( )
    cdf_m = np.ma.masked_equal(cdf, 0)
    cdf_m = (cdf_m - cdf_m.min( )) * 255 / (cdf_m.max( ) - cdf_m.min( ))
    # cdf = np.ma.filled(cdf_m, 0).astype('uint8')

    plt.plot(cdf_m, color = 'k')  # plot cdf mask
    plt.plot(cdf_normalized, color = 'b')  # plot normalized cdf

    plt.hist(gray.flatten( ), 256, [0, 256], color = 'r')  # src gray image hist
    plt.hist(dst.flatten( ), 256, [0, 256], color = 'y')  # dst equalized gray image hist

    plt.xlim([0, 256])
    plt.legend(('cdf_m', 'cdf_normalized', 'src histogram', "equalized hist"), loc = 'upper left')
    plt.show( )
    cv.imshow("clahe_demo", dst)
    cv.imwrite("../pictures/clahe_lena.png", dst)


def create_rgb_hist(image):
    """

    :param image:
    :return:
    """
    h, w, c = image.shape
    rgbHist = np.zeros([16 * 16 * 16, 1], np.float32)
    bsize = 256 / 16
    for row in range(h):
        for col in range(w):
            b = image[row, col, 0]
            g = image[row, col, 1]
            r = image[row, col, 2]
            index = np.int(b / bsize) * 16 * 16 + np.int(g / bsize) * 16 + np.int(r / bsize)
            rgbHist[np.int(index), 0] = rgbHist[np.int(index), 0] + 1
    return rgbHist


def hist_compare(image1, image2):
    """

    :param image1:
    :param image2:
    :return:
    """
    hist1 = create_rgb_hist(image1)
    hist2 = create_rgb_hist(image2)
    match1 = cv.compareHist(hist1, hist2, cv.HISTCMP_BHATTACHARYYA)
    match2 = cv.compareHist(hist1, hist2, cv.HISTCMP_CORREL)
    match3 = cv.compareHist(hist1, hist2, cv.HISTCMP_CHISQR)
    print("巴氏距离: %s, 相关性: %s, 卡方: %s" % (match1, match2, match3))


def main():
    """
    主调函数
    :return:
    """

    img_path = "../pictures/lena-noisy.png"  # input image path

    # 检查图像读取路径合法性
    if not (os.path.exists(img_path) and
            os.access(img_path, os.R_OK)):
        # 判断文件路径是否存在; 检查文件是否可读
        print(img_path + ' ' + 'have problem!')
        print("ERROR : File is not exists or un-accessible to read")
    else:
        print("--------------------------")
        print("File is accessible to read")
        print("---------Histogram Plot Python ---------")
        window_name = "Input Image"
        cv.namedWindow(window_name, cv.WINDOW_AUTOSIZE)
        image_lena = cv.imread(img_path)
        cv.imshow(window_name, image_lena)

        # histogram comp
        image1 = cv.imread("../pictures/lena.png")
        image2 = cv.imread("../pictures/lenanoise.png")

        #######
        hist_compare(image1, image2)
        #######

        #######
        # clahe_demo(image_lena)
        #######

        #######
        # equalHist_demo(image_lena)
        #######


        cv.waitKey(0)
        cv.destroyAllWindows( )  # destroy all windows


if __name__ == '__main__':
    main( )

彩色三通道图像->灰度图:

clahe自适应灰度均衡对比全局灰度均衡:

clahe局部自适应灰度均衡统计直方图:

全局自适应灰度均衡统计直方图:

 

2. 直方图比较


def create_rgb_hist(image):
    """

    :param image:
    :return:
    """
    h, w, c = image.shape
    rgbHist = np.zeros([16 * 16 * 16, 1], np.float32)
    bsize = 256 / 16
    for row in range(h):
        for col in range(w):
            b = image[row, col, 0]
            g = image[row, col, 1]
            r = image[row, col, 2]
            index = np.int(b / bsize) * 16 * 16 + np.int(g / bsize) * 16 + np.int(r / bsize)
            rgbHist[np.int(index), 0] = rgbHist[np.int(index), 0] + 1
    return rgbHist


def hist_compare(image1, image2):
    """

    :param image1:
    :param image2:
    :return:
    """
    hist1 = create_rgb_hist(image1)
    hist2 = create_rgb_hist(image2)
    match1 = cv.compareHist(hist1, hist2, cv.HISTCMP_BHATTACHARYYA)
    match2 = cv.compareHist(hist1, hist2, cv.HISTCMP_CORREL)
    match3 = cv.compareHist(hist1, hist2, cv.HISTCMP_CHISQR)
    print("巴氏距离: %s, 相关性: %s, 卡方: %s" % (match1, match2, match3))

 

巴氏距离: 0.09072200336618969, 相关性: 0.9788106004024394, 卡方: 164.3082768373199

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Techblog of HaoWANG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值