OpenVINOTM,给你看得见的未来!>>
电子产品世界 » 论坛首页 » 企业专区 » OpenVINO生态社区 » 【原创】OpenCV-Python系列之直方图简述(三十四)

共4条 1/1 1 跳转至

【原创】OpenCV-Python系列之直方图简述(三十四)

高工
2020-07-15 21:21:03    评分

OpenCV-Python系列之直方图简述

自本教程开始,我们开始系统的讨论OpenCV中的直方图,图像的直方图也是一个庞大的体系,我们将会用多次教程来讲述。

首先,我们来介绍下直方图是什么,我们可以将直方图视为一个图形或图表,它可以让我们对图像的强度分布有一个大致的了解。而该图形是一个x 轴上像素值(范围从0255,但并不总是)和y 轴上有相应像素数的图像。

可以说,它是另一种对图像理解的方式。通过观察图像的直方图,我们可以得到关于图像的相反,亮度,强度分布等直观信息。如今现在几乎所有的图像处理工具都可以提供其图像直方图的特征。下面是一个从Cambridge in Color website 获取的图像,有条件的小伙伴可以访问这个网站了解更多的细节。

     image.png

上图中我们可以看到原图像及其直方图。(注,这里的直方图是在灰度图像基础上绘制的,而不是彩色图像)。直方图的左区域显示较暗(pixel value比较小)的像素在图像中的数量,而右区域则显示了较亮(pixel value比较大)像素的数量。从上面的柱状图中我们可以看到黑暗的区域比亮区域大,还有中间色调(像素值在中档,值在127附近)的数量非常少。

寻找直方图

现在我们粗略了解了什么是直方图,我们接下去考虑如何找到直方图呢。无论 OpenCV 还是 Numpy 都带有内置函数。在使用这些函数之前,我们先需要了解一下和直方图相关的术语。

BINS:上面我们举例子用的直方图显示了每个像素的像素值范围,即从0255,这很有帮助如果我们的直方图需要全部这256个像素值。但是,假如我们并不需要一整个拥有所有像素值的范围,而是一部分一部分分隔开来的像素值区间呢?比如说,我们需要区间在0151631..... 240255的像素的数量。那么我们只需要16个值就可以代表这个直方图了。

所以我们接下去要做的仅仅是把整个直方图分为16个部分(sub-part)并保证每个部分的值相加起来就是所有像素加起来的总和。这里的每个部分我们称之称为“BIN”。在一开始的例子中,BIN 数量为256(每一个数字代表一个像素),而在我们稍后分隔后的例子里,BIN 的数量就是16 了。BIN 用来代表OpenCV 文档中的术语histSize

DIMS:这是我们收集的数据有关参数的个数。在上面这个例子中,我们收集数据只关心一个参数,强度值,这里是1

RANGE:这是我们想要测量的强度值的范围。通常情况下,范围是 [0256],即所有的强度值。

图像中的直方图计算有两种方式,我们将先介绍使用OpenCV中的直方图计算。

OpenCV 中的直方图计算

现在让我们用cv2.calcHist() 函数来找到直方图,先来熟悉下该函数及其参数:

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

图像 images:它是uint8 float32 类型的源图像,可以用方括号表示,即“[img]”。

通道 channels:它也可以由方括号来表示,可以说是我们计算直方图通道的索引。例如,如果输入的是灰度图像,则其值为[0];而对于彩色图像,则可以通过[0][1][2]分别计算蓝,绿,红通道的直方图。

掩膜 mask:掩膜图像。为了找到完整图像的直方图,我们将其设为“None”。但如果在未来想找到图像某特定区域的直方图,我们可以为它创建一个掩码图像并将其设为mask(文章后面将会展示一个示例。)

histSize:这用来表示我们的BIN 数量,需要用方括号表示。对于全比例图像,我们则设置其值为 [256]

范围 ranges:这用来表示我们的范围,通常是[0256]

现在让我们从一个样本图像开始,在灰度模式下加载图像并找到它的完整直方图:

image.png

代码如下:

def calhist(img):
    hist = cv2.calcHist([img],[0],None,[256],[0,256])
    print(hist)

image.png

这样我们得到的hist 就是一个256 x1 的数组,数组中的每个值对应于图像中像素的数量和其对应的像素值。

Numpy 中的直方图计算

Numpy 也为我们提供一个函数,np.histogram(),用来取代calcHist() 函数的作用,我们可以试试下面这行代码:

def npcalhist(img):
    hist, bins = np.histogram(img.ravel(), 256, [0, 256])
    print(hist)

image.png

这里的hist 计算方法和我们之前教程中使用的一样。区别是这儿的bins 257个元素,因为Numpy 是这么计算bins 的,0 - 0.991 - 1.992 - 2.99 ... 这样,最后的范围则是255 - 255.99。为了257 这样的表示,我们在bins 的尾端增加256;但其实我们不需要256,所以实际上到255 就足够了。

Numpy 还有另一个函数np.bincount(),它比np.histogram() 函数快得多,快了将近10倍。因此对于一维直方图,我们可以用bincount() 这个函数,但别忘了在np.bincount() 中设置minlength = 256。例如:

hist = np.bincount(img.ravel()minlength=256).

但是,OpenCV函数比histogram() 还要更快(约40倍),所以还是尽可能用OpenCV 函数吧。

  好了,知道了怎么寻找直方图,接下来我们应该学会怎么绘制,但是要怎么做呢?

绘制直方图

       我们有两种方法绘制:

·           简捷的方法:使用Matplotlib 绘图函数

·           复杂一点的方法:使用OpenCV 绘图功能

使用Matplotlib

Matplotlib 附带了一个直方图绘制函数:matplotlib.pyplot.hist()

它可以直接找到直方图并绘制,我们就不需要使用calcHist() np.histogram() 函数来预先找到直方图。看下面的代码:

from matplotlib import pyplot as plt#导入Matplotlib库
 
def matdraw(img):
    plt.hist(img.ravel(), 256, [0, 256])
    plt.show()

image.png

或者我们可以使用正常普通的matplotlib 绘图功能,这将有利于BGR 色彩绘图。如果要这么做,首先我们需要找到直方图数据。我们来看代码:

def RGBdraw(img):
    color = ('b', 'g', 'r')
    for i, col in enumerate(color):
        histr = cv2.calcHist([img], [i], None, [256], [0, 256])
        plt.plot(histr, color=col)
        plt.xlim([0, 256])
    plt.show()

image.png

这样可以更直观的看RGB三个通道的直方图。

由于OpenCV的绘制直方图相当的繁琐,在这里就不介绍了,通常我们并不使用OpenCV的函数来生成直方图。

下一个教程我们将讲述直方图的其他应用。

 




工程师
2020-07-16 08:23:12    评分
2楼

讲得很容易懂


助工
2020-07-16 08:41:04    评分
3楼

感谢你的分享


高工
2020-07-17 09:26:35    评分
4楼

详实的资料,学习新知识的首选


共4条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]