OpenCV-直方图

文章目录

  • 直方图(histogram)
    • numpy.ravel
    • enumerate
  • 绘制直方图
    • matplotlib.pyplot.hist
    • 示例
  • 计算图像直方图
    • cv2.calcHist
    • 示例
    • ==错误记录==
  • 直方图应用
    • 直方图均衡化
      • cv2.equalizeHist
      • 示例
    • 局部直方图均衡化
      • cv2.createCLAHE
      • 示例
    • 直方图比较
      • cv2.compareHist
      • 示例
      • 直方图比较中的bins如何理解
      • ==错误记录==
  • 二维直方图
    • 示例
  • 归一化与标准化
    • cv2.normalize
  • 直方图反向投影
    • cv2.calcBackProject
    • 示例

直方图(histogram)

直方图是对图像像素的统计分布,它统计了每个像素(0到L-1)的数量。

注意:
pycharm从2017.3版之后,将matplotlib的绘图的结果默认显示在SciView窗口中,而不是弹出独立的窗口。

修改方式见链接:
新版Pycharm中Matplotlib图像不在弹出独立的显示窗口

numpy.ravel

将多维数组降为一维
可以传入索引:‘C’, ‘F’, ‘A’, ‘K’

参考链接:
numpy 辨异 (五)—— numpy.ravel() vs numpy.flatten()

enumerate

枚举

参考链接:

  • python中的for循环总结(enumerate)
  • for i in enumerate(): 解析

绘制直方图

matplotlib.pyplot.hist

绘制直方图,一般用来绘制灰度直方图

def hist(
        x, bins=None, range=None, density=False, weights=None,
        cumulative=False, bottom=None, histtype='bar', align='mid',
        orientation='vertical', rwidth=None, log=False, color=None,
        label=None, stacked=False, *, data=None, **kwargs):
    return gca().hist(
        x, bins=bins, range=range, density=density, weights=weights,
        cumulative=cumulative, bottom=bottom, histtype=histtype,
        align=align, orientation=orientation, rwidth=rwidth, log=log,
        color=color, label=label, stacked=stacked,
        **({"data": data} if data is not None else {}), **kwargs)

示例

注意: 此处还没有将图像转换为灰度图

from matplotlib import pyplot as plt

def plot(image):
    """画出image的直方图"""
    # image.ravel()将图像展开为一维数组,256为bins数量,[0, 256]为数值范围(不包括256)
    plt.hist(image.ravel(), 256, [0, 256])
    plt.show()  # 显示直方图

结果:
在这里插入图片描述

计算图像直方图

cv2.calcHist

计算图像直方图

cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate ]]) ->hist

  • images:输入图像,参数必须用方括号括起来。
  • channels:计算直方图的通道。
  • Mask:掩膜,一般用None,表示处理整幅图像。
  • histSize:表示这个直方图分成多少份(即多少个直方柱)。
  • range:直方图中各个像素的值,[0.0,256.0]表示直方图能表示像素值从0.0到256的像素。
  • hist:是一个256x1阵列,每个值对应于该图像中的像素值及其对应的像素值
  • accumulate:是一个布尔值,用来表示直方图是否叠加。

注意:

  • 最后是两个可选参数,由于直方图作为函数结果返回了。
  • 除了mask,其他四个参数都要带[]号

示例

绘制image的直方图(BGR三个通道)

def image_hist(image):
    """绘制image的直方图(三个通道)"""
    color = ('blue', 'green', 'red')
    for i, color in enumerate(color):  # # enumerate 枚举,返回元素以及对应的索引
        # 计算出直方图,calcHist(images, channels, mask, histSize(有多少个bin), ranges[, hist[, accumulate]]) -> hist
        # hist 是一个 256x1 的数组,每一个值代表了与该灰度值对应的像素点数目。
        hist = cv.calcHist(image, [i], None, [256], [0, 256])
        # print(hist.shape)
        plt.plot(hist, color=color)  #  绘制函数曲线
        plt.xlim([0, 256])  # 设置坐标轴刻度取值范围
    plt.show()

结果:
在这里插入图片描述
参考链接:

  • python 可视化:fig, ax = plt.subplots()画多表图的3中常见样例 & 自定义图表格式
  • plt.imshow()为什么不能显示同时显两张照片
  • matplotlib.pyplot保存多张图片覆盖问题

错误记录

问题一:

在按照https://blog.csdn.net/u010472607/article/details/82290159点击查看的操作,让图表单独显示后,产生了图框出现而内容不出现的问题,如下图所示:
在这里插入图片描述
原因:未知,个人猜测是交互模式和阻碍模式的问题(可能性高),或是线程阻碍产生的结果
解决方案:
第一种:

  1. 注释掉主函数里的cv.imshow()
    # cv.imshow("example", src1)
    plot(src1)

第二种:

  1. 给plt.show传入False参数
plt.show(False)

第三种(推荐):

  1. 在plt.show之前添加plt.ioff函数,关闭交互模式。

第四种(推荐):

  1. 命令行里运行(命令行默认是阻塞模式)(按道理交互模式可以显示多张图,阻塞模式不行。但是现在情况与文档说明完全相反,不一致),或者直接在设置里改成终端运行

参考链接:

  • python matplotlib.pyplot.show() plt.show()(显示一个图表)
  • matplotlib.pyplot 中显示图像的两种模式(交互和阻塞)及其在Python画图中的应用
  • pycharm两个交互模式
  • matplotlib中ion()和ioff()的使用
  • matplot.show() 阻塞程序怎么解决

问题二:
在出现问题一并解决后,想要同时显示两张图表,却发现两张图只显示前一张
原因: 显示第一张的时候就用了plt.show
解决方案:
第一种:

  1. 使用pyplot.savefig保存图片和pyplot.clf清除图表内容,防止两张图合为一张

第二种:

  1. 使用别的绘图指令,产生不同的图表对象再一起绘图

直方图应用

直方图均衡化

直方图均衡化就是将原始的直方图拉伸,使之均匀分布在全部灰度范围内,从而增强图像的对比度。

直方图均衡化的中心思想是把原始图像的的灰度直方图从比较集中的某个区域变成在全部灰度范围内的均匀分布。

如果一幅图像的灰度直方图几乎覆盖了整个灰度的取值范围,并且除了个别灰度值的个数较为突出,整个灰度值分布近似于均匀分布,那么这幅图像就具有较大的灰度动态范围和较高的对比度,同时图像的细节更为丰富。

均衡化步骤:

  • 统计直方图中每个灰度级出现的次数;
  • 计算累计归一化直方图;
  • 重新计算像素点的像素值

参考链接:

  • 直方图均衡化

注意:
OpenCV里的直方图均衡化都是基于灰度图

cv2.equalizeHist

直方图均衡化

equalizeHist(src[, dst]) -> dst
  • src:8位单通道图像

示例

直方图均衡化

def equal_hist(image):
    """全局直方图均衡化,用于增强图像对比度,即黑的更黑,白的更白"""
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    dst = cv.equalizeHist(gray)
    cv.imshow("equalHist", dst)

结果:
在这里插入图片描述

局部直方图均衡化

直方图均衡化可以可能到是一种全局意义上的均衡化,但是有的时候这种操作并不是很好,会把某些不该调整的部分给调整了。OpenCV中还有一种直方图均衡化,它是一种局部局部来的均衡化,也就是是说把整个图像分成许多小块(比如按10*10作为一个小块),那么对每个小块进行均衡化。这种方法主要对于图像直方图不是那么单一的(比如存在多峰情况)图像比较实用。

cv2.createCLAHE

自适应均衡化图像

createCLAHE([, clipLimit[, tileGridSize]]) -> retval
  • clipLimit颜色对比度的阈值
  • titleGridSize进行像素均衡化的网格大小,即在多少网格下进行直方图的均衡化操作

注意:

  • 用法与equalizeHist不一样。createCLAHE返回的是一个实例化的对象。需要调用apply函数才能完成均衡化操作。

示例

局部直方图均衡化

def local_equal_hist(image):
    """局部直方图均衡化"""
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    # clipLimit颜色对比度的阈值,titleGridSize进行像素均衡化的网格大小,即在多少网格下进行直方图的均衡化操作
    local_hist = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))  # 实例化均衡直方图函数
    local_hist_dst = local_hist.apply(gray)
    cv.imshow("local_equal_hist", local_hist_dst)

结果:
在这里插入图片描述
参考链接:

  • 机器学习进阶-直方图与傅里叶变化-直方图均衡化
  • python中 apply()函数的用法

参考链接:

  • Python+OpenCV图像处理(八)—— 图像直方图
  • numpy 辨异 (五)—— numpy.ravel() vs numpy.flatten()
  • 数字图像处理(15): 灰度直方图(matplotlib 和OpenCV 绘制直方图)
  • Python可视化库matplotlib(基础整理)
  • 直方图均衡化
  • 机器学习进阶-直方图与傅里叶变化-直方图均衡化
  • python中 apply()函数的用法

直方图比较

如果我们有两张图像,并且这两张图像的直方图一样,或者有极高的相似度,那么在一定程度上,我们可以认为这两幅图是一样的,这就是直方图比较的应用之一。

cv2.compareHist

直方图比较

compareHist(H1, H2, method) -> retval
  • H1,H2 :分别为要比较图像的直方图
  • method :比较方式

比较方式有:

  • 相关性(method=cv.HISTCMP_CORREL) 值越大,相关度越高,最大值为1,最小值为0
  • 卡方(method=cv.HISTCMP_CHISQR)值越小,相关度越高,最大值无上界,最小值0
  • 巴氏距离(method=cv.HISTCMP_BHATTACHARYYA)值越小,相关度越高,最大值为1,最小值为0
  • 相交性(method=HISTCMP_INTERSECT)取两个直方图每个相同位置的值的最小值,然后求和,这个比较方式不是很好,不建议使用

==注意: ==
用巴氏距离和相关性比较好

参考链接:

  • Python-Opencv中用compareHist函数进行直方图比较进行对比图片
  • OpenCV-图像处理(25、直方图比较)
  • 【opencv学习笔记】026之直方图比较 - compareHist函数详解
  • 相似性度量(Similarity Measurement)与“距离”(Distance)

示例

利用直方图比较图像相似性

def create_rgbhist(image):
    """创建直方图"""
    h, w, c = image.shape
    rgbHist = np.zeros([16 * 16 * 16, 1], np.float32)  # 每个通道的灰度值(0~255共256个值)分为16个刻度
    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]
            # 单一通道的灰度值依据刻度的宽度归到16个刻度内得到对应的值
            # 像素点的BGR三通道分别依据得到的值,按权重取得索引(就是三通道的值排到一起了)
            # 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  # 对应索引处的值(频数)+1
            index = int(b / bsize) * 16 * 16 + int(g / bsize) * 16 + int(r / bsize)
            rgbHist[int(index), 0] = rgbHist[int(index), 0] + 1  # 对应索引处的值(频数)+1

    return rgbHist


def hist_compare(image1, image2):
    """利用直方图比较相似性"""
    hist1 = create_rgb_demo(image1)
    hist2 = create_rgb_demo(image2)
    match1 = cv.compareHist(hist1, hist2, method=cv.HISTCMP_BHATTACHARYYA)
    match2 = cv.compareHist(hist1, hist2, method=cv.HISTCMP_CORREL)
    match3 = cv.compareHist(hist1, hist2, method=cv.HISTCMP_CHISQR)
    print("巴式距离:%s, 相关性:%s, 卡方:%s" % (match1, match2, match3))

结果:
SRC1
在这里插入图片描述
SRC2
在这里插入图片描述
计算结果:
在这里插入图片描述
注意:
上述比较的代码只能用于两张大小一样的图片,如果需要比较两张不一样
大小的图片,需要进行归一化操作

直方图比较中的bins如何理解

把RGB颜色空间,想象成一个三维立体的坐标系,rgb对应xyz轴,每个颜色8 bins,对应xyz三个轴上,8个等分刻度,这样就得到一个8x8x8=512个小立方体构成的大立方体,要的直方图就是每个小立方体在大立方体中出现的概率分布。

参考链接:

  • 关于颜色直方图中bins的理解
  • 直方图中bins应如何理解及处理
  • Python-Opencv中用compareHist函数进行直方图比较进而对比图片

错误记录

按照教程调用hist_compare函数的时候显示:

`np.int` is a deprecated alias for the builtin `int`. To silence this warning, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.

原因:Python不推荐使用np.int
解决方案:将np.int修改为int

二维直方图

一维直方图,需要从BGR转换为灰度,二维直方图,需要将图像从BGR转换为HSV。

示例

def hist2d(image):
    """绘制图像的二维直方图"""
    hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV)
    hist = cv.calcHist([hsv], [0, 1], None, [180, 360], [0, 180, 0, 256])  # 计算H和S通道的2D直方图
    print(hist.shape)
    # cv.imshow("hist2d", hist)
    plt.imshow(hist, interpolation="nearest")  # 直方图显示,插值方法为最近领域内插法
    plt.title("2D Histogram")
    plt.ioff()
    plt.show()

结果:
在这里插入图片描述
注意:
interpolation='nearest’如果显示分辨率与图像分辨率不同(通常是这种情况),则只显示图像而不尝试在像素之间进行插值。它将生成一个像素显示为多个像素的正方形的图像。

参考链接:

  • plt.imshow()
  • 数字图像处理笔记二 - 图片缩放(最近邻插值(Nearest Neighbor interpolation))

归一化与标准化

cv2.normalize

归一化(矩阵的值通过某种方式变到某一个区间内)

normalize(src, dst[, alpha[, beta[, norm_type[, dtype[, mask]]]]]) -> dst

src-输入数组。
dst-与SRC大小相同的输出数组。
α-范数值在范围归一化的情况下归一化到较低的范围边界。
β-上限范围在范围归一化的情况下;它不用于范数归一化。
norm_type-规范化类型,见下面参考链接。
dType——当输出为负时,输出数组具有与SRC相同的类型;否则,它具有与SRC相同的信道数和深度=CVH-MatthAsHead(DyType)。
mask-可选的操作掩膜。

参考链接:

  • 标准化和归一化,请勿混为一谈,透彻理解数据变换
  • 标准化和归一化什么区别?
  • opencv中归一化函数normalize()的原理讲解
  • opencv中归一化函数cv2.normalize()的原理讲解

直方图反向投影

cv2.calcBackProject

直方图反向投影

calcBackProject(images, channels, hist, ranges, scale[, dst]) -> dst

参数与cv2.calchist几乎相同

注意:
在传递给backproject函数之前,应该对对象直方图进行归一化。

示例

def back_projection(sample, target):
    """直方图反向投影,sample是样本,target是需要寻找的输入图像"""
    roi_hsv = cv.cvtColor(sample, cv.COLOR_BGR2HSV)
    target_hsv = cv.cvtColor(target, cv.COLOR_BGR2HSV)

    cv.imshow("sample", sample)
    cv.imshow("target", target)

    roiHist = cv.calcHist([roi_hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])

    # 归一化:原始图像,结果图像,映射到结果图像中的最小值,最大值,归一化类型
    # cv.NORM_MINMAX对数组的所有值进行转化,使它们线性映射到最小值和最大值之间
    # 归一化后的图像便于显示,归一化后到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)

结果:
在这里插入图片描述

你可能感兴趣的:(图像处理,OpenCV,opencv,python)