图像直方图(英语:Image Histogram) 是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数。可以借助观察该直方图了解需要如何调整亮度分布。这种直方图中,横坐标的左侧为纯黑、较暗的区域,而右侧为较亮、纯白的区域。因此,一张较暗图片的图像直方图中的数据多集中于左侧和中间部分;而整体明亮、只有少量阴影的图像则相反。

图像2D直方图:我们已经计算并绘制了一维直方图,因为我们只考虑一个特征,即像素的灰度强度值.但在二维直方图中,需要考虑两个特征,通常,它用于查找颜色直方图,其中两个要素是每个像素的色调和饱和度值。

解释:颜色直方图是在许多图像检索系统中被广泛采用的颜色特征。它所描述的是不同色彩在整幅图像中所占的比例,而并不关心每种色彩所处的空间位置,即无法描述图像中的对象或物体。颜色直方图特别适于描述那些难以进行自动分割的图像。

直方图应用: 直方图均衡化:提升对比度的两种方法:默认、自定义

直方图均衡化:直方图均衡化是一种简单有效的图像增强技术,通过改变图像的直方图来改变图像中各像素的灰度,主要用于增强动态范围偏小的图像的对比度。

直方图反向投影:它可以用来做图像分割,或者在图像中找寻我们感兴趣的部分。简单来说,它会输出与输入图像(待搜索)同样大小的图像,其中的每一个像素值代表了输入图像上对应点属于目标对象的概率。用更简单的话来解释,输出图像中像素值越高(越白)的点就越可能代表我们要搜索的目标(在输入图像所在的位置)。这是一个直观的解释。

函数:calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate ]])

images: 原图像(图像格式为uint8 或float32)。当传入函数时应该用中括号[] 括起来,例如:[img]。
channels: 同样需要用中括号括起来,它会告诉函数我们要统计那幅图像的直方图。如果输入图像是灰度图,它的值就是[0];如果是彩色图像的话,传入的参数可以是[0],[1],[2] 它们分别对应着通道B,G,R。
mask: 掩模图像。要统计整幅图像的直方图就把它设为None。但是如果你想统计图像某一部分的直方图的话,你就需要制作一个掩模图像,并使用它。(后边有例子)
histSize:BIN 的数目。也应该用中括号括起来,例如:[256]。
ranges: 像素值范围,通常为[0,256]如果要绘制颜色直方图的话,我们首先需要将图像的颜色空间从BGR 转换到HSV。(记住,计算一维直方图,要从BGR 转换到HSV)。计算2D 直方图,函数的参数要做如下修改:
• channels=[0,1] 因为我们需要同时处理H 和S 两个通道。
• bins=[180,256]H 通道为180,S 通道为256。
• range=[0,180,0,256]H 的取值范围在0 到180,S 的取值范围在0 到256。

Numpy 同样提供了绘制2D 直方图的函数:np.histogram2d():

下面展示 代码

import cv2 as cv
from matplotlib import pyplot as plt


# 画出图像的直方图
def hist_image(image):
    color = ("blue", "green", "red")
    for i, color in enumerate(color):
        hist = cv.calcHist([image], [i], None, [256], [0, 256])
        # 绘制图像直方图
        plt.plot(hist, color=color)
        # 设置x轴的数值显示范围
        plt.xlim([0, 256])
    #     展示直方图
    plt.show()


# 提升对比度(默认提升),只能是灰度图像
def equalHist_image(image):
    # 转换为灰度图
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    cv.imshow("original", gray)  # 因为只能处理灰度图像,所以输出原图的灰度图像用于对比
    # 直方图均衡化
    dst = cv.equalizeHist(gray)
    # 默认处理
    cv.imshow("default_processing", dst)


# 对比度限制(自定义提示参数)
def clahe_image(image):
    # 转换为灰度图
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    # 自适应直方图均衡化
    clahe = cv.createCLAHE(clipLimit=4.0, tileGridSize=(4, 4))  # clipLimit是对比度的大小,tileGridSize是每次处理块的大小
    dst = clahe.apply(gray)
    # 自定义处理
    cv.imshow("custom_processing", dst)


#直方图反向投影
def back_projection_demo():
    # 样图
    sample = cv.imread("data/green.jpg")
    # 目标图
    target = cv.imread("data/messi5.jpg")
    roi_hsv = cv.cvtColor(sample, cv.COLOR_BGR2HSV)#转到hsv色彩空间
    target_hsv = cv.cvtColor(target, cv.COLOR_BGR2HSV)

    # 显示图片
    cv.imshow("sample", sample)
    cv.imshow("target", target)
    # 用calcHist来找直方图(也可以用np.histogram2d) ,[0, 1], 计算H,D两个通道,[32, 32]:bins,可以通过调整它改变结果
    roiHist = cv.calcHist([roi_hsv], [0, 1], None, [32, 32], [0, 180, 0, 256])#[0, 180, 0, 256]:h s的取值范围
    # 归一化之后的直方图便于显示,归一化之后就成了 0 到 255 之间的数了。
    cv.normalize(roiHist, roiHist, 0, 255, cv.NORM_MINMAX)
    dst = cv.calcBackProject([target_hsv], [0, 1], roiHist, [0, 180, 0, 256], 1)

    cv.imshow("backProjectionDemo", dst)

#2d直方图
def hist2d_demo(image):
    # 转到hsv色彩空间
    hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV)
    # 这里[0,1]是绘制了H,S通道的直方图
    hist = cv.calcHist([hsv], [0, 1], None, [32, 32], [0, 180, 0, 256])
    #cv.imshow("hist2d", hist)
    plt.imshow(hist, interpolation='nearest')
    plt.title("2D Histogram")
    plt.show()

if __name__ == '__main__':
    '''
    hist_image()函数
    '''
    # src = cv.imread("pic/cat.jpg")
    # cv.namedWindow("original", cv.WINDOW_NORMAL)
    # cv.imshow("original", src)
    # hist_image(src)
    # cv.waitKey(0)
    # cv.destroyAllWindows()

    '''
    equalHist_image()函数
    '''
    # src = cv.imread("pic/cat.jpg")
    # cv.namedWindow("original", cv.WINDOW_NORMAL)
    # cv.imshow("original", src)
    # equalHist_image(src)
    # cv.waitKey(0)
    # cv.destroyAllWindows()

    '''
    clahe_image()函数
    '''
    # src = cv.imread("pic/cat.jpg")
    # cv.namedWindow("original", cv.WINDOW_NORMAL)
    # cv.imshow("original", src)
    # clahe_image(src)
    # cv.waitKey(0)
    # cv.destroyAllWindows()

    '''
    hist2d_demo()函数
    '''
    # src = cv.imread("pic/cat.jpg")
    # cv.namedWindow("original", cv.WINDOW_NORMAL)
    # cv.imshow("original", src)
    # hist2d_demo(src)
    # cv.waitKey(0)
    # cv.destroyAllWindows()

    '''
    back_projection_demo()函数
    '''
    back_projection_demo()
    cv.waitKey(0)
    cv.destroyAllWindows()