OpenCV中的图像处理3.10(八)直方图-寻找、绘制、分析(掩膜)与均衡化

news/2024/7/21 6:38:51 标签: opencv, 图像处理, 计算机视觉, 人工智能, python

目录

  • 3.10 OpenCV中的直方图
    • 3.10.1 直方图--1:寻找、绘制、分析
      • 目标
      • 理论
      • 寻找直方图
      • 绘制直方图
      • 掩膜的应用
      • 其他资源
    • 3.10.2 直方图--2:直方图均衡化
      • 目标
      • 理论
      • OpenCV中的直方图均衡化
      • CLAHE(对比度有限的自适应直方图均衡)
      • 其他资源

翻译及二次校对:cvtutorials.com

编辑者:廿瓶鲸(和鲸社区Siby团队成员)

3.10 OpenCV中的直方图

3.10.1 直方图–1:寻找、绘制、分析

目标

学习:

  • 使用OpenCV和Numpy函数寻找直方图
  • 使用OpenCV和Matplotlib函数绘制直方图
  • 你将看到这些函数:cv.calcHist(), np.histogram()等。

理论

那么什么是直方图?你可以把直方图看作是一种图,它可以让你对图像的灰度分布有一个整体的了解。它是一个在X轴上有像素值(范围从0到255,不一定),在Y轴上有图像中相应像素数的图。

它只是理解图像的另一种方式。通过观察图像的直方图,你可以获得关于该图像的对比度、亮度、灰度分布等的直觉。今天,几乎所有的图像处理工具都提供直方图的功能。下面是一张来自Cambridge in Color网站的图片,我建议你访问该网站了解更多细节。

Image Name

你可以看到这个图像和它的直方图(这个直方图是为灰度图像绘制的,不是彩色图像)。直方图的左边区域显示图像中较暗像素的数量,右边区域显示较亮像素的数量。从直方图中,你可以看到暗色区域比亮色区域多,中间色调(中间范围的像素值,例如127左右)的数量非常少。

寻找直方图

现在我们对什么是直方图有了一个概念,我们可以研究如何找到它。OpenCV和Numpy都有内置的函数来完成这个任务。在使用这些函数之前,我们需要了解一些与直方图有关的术语。

BINS :上面的直方图显示了每个像素值的像素数,即从0到255,即你需要256个值来显示上述直方图。但请考虑,如果你不需要单独找到所有像素值的像素数,而是需要找到像素值的一个区间的像素数,怎么办?例如,你需要找到0到15之间的像素数,然后是16到31,…,240到255。你只需要16个值来表示直方图。

所以你要做的就是简单地把整个直方图分成16个子部分,每个子部分的值是其中所有像素数的总和。这个子部分被称为 “BIN”。在第一种情况下,BIN的数量是256(每个像素一个),而在第二种情况下,它只有16。在OpenCV的文档中,BINS是由术语histSize表示的。

DIMS : 它是我们收集数据的参数数量。在这种情况下,我们只收集一种数据,即灰度值。所以这里是1。

RANGE : 它是你想测量的灰度值的范围。通常情况下,它是[0,256],即所有灰度值。

1.OpenCV中的直方图计算

所以现在我们使用cv.calcHist()函数来寻找直方图。让我们熟悉一下这个函数和它的参数。

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

  • images : 它是类型为uint8或float32的源图像,应在方括号内给出,即"[img]"。
  • channels : 它也是在方括号中给出的。它是我们计算直方图的通道的索引。例如,如果输入的是灰度图像,它的值是[0]。对于彩色图像,你可以通过[0]、[1]或[2]来分别计算蓝色、绿色或红色通道的直方图。
  • mask : 掩膜图像。要查找完整图像的直方图,它被指定为 “None”。但如果你想找到图像特定区域的直方图,你必须为其创建一个掩膜图像并将其作为掩膜(我将在后面展示一个例子)
  • histSize:这代表我们的BIN计数。需要在方括号中给出。对于满刻度,我们传递256。
  • ranges : 这是我们的RANGE。通常情况下,它是[0,256]。

那么,让我们从一个图像开始。简单地在灰度模式下加载一个图像,然后找到它的直方图。

python">img = cv.imread('home.jpg',0)
hist = cv.calcHist([img],[0],None,[256],[0,256])

hist是一个256x1的数组,每个值都对应于该图像中的像素数和其对应的像素值。

2.Numpy中的直方图计算

Numpy还为你提供了一个函数,np.histogram()。所以你可以用下面这行来代替calcHist()函数。

python">hist,bins = np.histogram(img.ravel(),256,[0,256])

hist与我们之前计算的一样。但是bins会有257个元素,因为Numpy计算的bins是0-0.99,1-1.99,2-2.99等等。所以最终的范围将是255-255.99。为了表示这一点,他们还在bins的末尾添加了256。但我们不需要那256。到255就足够了。

注意:Numpy还有一个函数,np.bincount(),它比np.histogram()快得多(大约10倍)。所以对于一维直方图,你最好试试这个。不要忘记在np.bincount中设置minlength = 256。例如,hist = np.bincount(img.ravel(),minlength=256) OpenCV函数比np.histogram()快(大约40倍)。所以请使用OpenCV函数。

现在我们应该绘制直方图,但如何绘制呢?

绘制直方图

有两种方法可以做到这一点。

  • 方法1:使用Matplotlib的绘图函数
  • 方法2:使用OpenCV绘图函数

1.使用Matplotlib

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

它直接找到直方图并绘制出来。你不需要使用calcHist()或np.histogram()函数来寻找直方图。请看下面的代码。

python">import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg',0)
plt.hist(img.ravel(),256,[0,256]); plt.show()

你会得到一个如下的图。

Image Name

或者你可以使用matplotlib的正常绘图模式,这对BGR图很有帮助。为此,你需要先找到直方图的数据。试试下面的代码。

python">import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg')
color = ('b', 'g', 'r')
for i,col in enumerate(color):
    histr = cv.calcHist([img],[i],None,[256],[0,256])
    plt.plot(histr,color = col)
    plt.xlim([0,256])
plt.show()

结果:

Image Name

你可以从上图中推断出,蓝色在图像中有一些高值区域(显然应该是由于天空的原因)。

2.使用OpenCV

我们可以将直方图的值和它的bin值调整成x,y坐标的样子,这样你就可以用cv.line()或cv.polyline()函数来绘制它,生成与上面相同的图像。这在OpenCV-Python2官方样本中已经有了。请查看样本/python/hist.py中的代码。

掩膜的应用

我们用cv.calcHist()来寻找全图的直方图。如果你想找到图像中某些区域的直方图呢?只要在你想找直方图的区域创建一个白色的蒙版图像,其他区域设置为黑色。然后把它作为掩膜传给你。

python">img = cv.imread('home.jpg',0)
# 创建一个掩膜
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv.bitwise_and(img,img,mask = mask)
# 计算带掩膜和不带掩膜的直方图
# 检查第三个参数的掩膜
hist_full = cv.calcHist([img],[0],None,[256],[0,256] )
hist_mask = cv.calcHist([img],[0],mask,[256],[0,256] )
plt.subplot(221), plt.imshow(img, 'grey')
plt.subplot(222), plt.imshow(mask,'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0,256])
plt.show()

请看结果。在直方图图中,蓝线显示的是完整图像的直方图,而绿线显示的是被遮蔽区域的直方图。

Image Name

其他资源

  • 彩色剑桥

3.10.2 直方图–2:直方图均衡化

翻译及二次校对:cvtutorials.com

目标

在本节中:

  • 我们将学习直方图均衡化的概念,并利用它来改善我们图像的对比度。

理论

考虑一个图像,其像素值只局限于某些特定的数值范围。例如,较亮的图像将有所有的像素限制在高值。但是一个好的图像会有来自图像所有区域的像素。因此,你需要将这个直方图拉伸到两端(如下图所示,来自维基百科),这就是直方图均衡化的作用(简单地说)。这通常会改善图像的对比度。

Image Name

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

python">import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('wiki.jpg',0)
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_normalized = cdf * float(hist.max()) / cdf.max()
plt.plot(cdf_normalized, color = 'b')
plt.hist(img.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])
plt.legend(('cdf','histogram'), loc = 'upper left')
plt.show()

Image Name

你可以看到直方图位于较亮的区域。我们需要完整的光谱。为此,我们需要一个转换函数,将较亮区域的输入像素映射到完整区域的输出像素。这就是直方图均衡化的作用。

现在我们找到直方图的最小值(不包括0),然后应用wiki页面中给出的直方图均衡化公式。但我在这里使用了Numpy中的掩膜数组概念。对于掩膜数组,所有的操作都是在非掩膜的元素上进行的。你可以从Numpy关于掩膜数组的文档中读到更多关于它的信息。

python">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')

现在我们有了查找表,它为我们提供了关于每个输入像素值的输出像素值的信息。所以我们只需应用转换。

python">img2 = cdf[img]

现在我们像以前一样计算它的直方图和Cdf(你来做),结果看起来像下面。

Image Name

另一个重要的特点是,即使图像是一个较暗的图像(而不是我们使用的一个较亮的图像),在均衡后,我们将得到与上述图像几乎相同的图像。因此,这被用作一个 “参考工具”,使所有图像具有相同的照明条件。这在许多情况下是很有用的。例如,在人脸识别中,在训练人脸数据之前,对人脸图像进行直方图均衡化,使其具有相同的照明条件。

OpenCV中的直方图均衡化

OpenCV有一个函数可以做到这一点,即cv.equalizeHist()。它的输入是灰度图像,输出是我们的直方图均衡化图像。

下面是一个简单的代码片段,显示了它在我们使用的同一图像上的用法。

python">img = cv.imread('wiki.jpg',0)
equ = cv.equalizeHist(img)
res = np.hstack((img,equ)) #stacking images side-by-side
cv.imwrite('res.png',res)

Image Name

因此,现在你可以在不同的光线条件下拍摄不同的图像,对其进行均衡并检查结果。

当图像的直方图被限制在一个特定的区域内时,直方图均衡化是好的。在灰度变化较大的地方,即直方图覆盖较大区域的地方,它不会有好的效果,也就是说,明亮和黑暗的像素都存在。请查看附加资源中的SOF链接。

CLAHE(对比度有限的自适应直方图均衡)

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

Image Name

诚然,在直方图均衡化之后,背景对比度得到了改善。但比较两张图片中的雕像的脸。由于过亮,我们失去了大部分的信息。这是因为它的直方图并不像我们在以前的案例中看到的那样被限制在一个特定的区域内(试着绘制输入图像的直方图,你会得到更多的直观感受)。

因此,为了解决这个问题,采用了自适应直方图均衡化。在这个过程中,图像被分成小块,称为 “瓦片”(OpenCV中瓦片大小默认为8x8)。然后这些块中的每一个都像往常一样被直方图均衡化。因此,在一个小区域内,直方图将被限制在一个小区域内(除非有噪声)。如果有噪音,它就会被放大。为了避免这种情况,采用了对比度限制。如果任何一个直方图仓超过了指定的对比度限制(在OpenCV中默认为40),在应用直方图均衡化之前,这些像素会被剪掉并均匀地分布到其他仓。在均衡化之后,为了消除瓦片边界的伪影,将应用双线性插值。

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

python">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)

请看下面的结果,并与上面的结果进行比较,特别是雕像区域。

Image Name

其他资源

1.维基百科关于直方图均衡化的页面
2.Numpy中的掩膜数组

还可以查看这些关于对比度调整的SOF问题。

1.我如何在OpenCV中用C语言调整对比度?
2.如何用opencv均衡图像的对比度和亮度?


http://www.niftyadmin.cn/n/338585.html

相关文章

知行之桥EDI系统2023版功能介绍——日志页面

在知行之桥EDI系统2023版中,除了在此前的文章中曾经介绍过的概览页面之外,还新增了日志页面。日志页面基于旧版本的状态页面进行了功能优化,为用户展示了消息、交易日志、应用程序日志、访问日志以及审计日志五种类型,每种日志类型…

测试的缺陷密度如何减少

测试的缺陷密度如何减少 随着软件开发的不断进步,测试已经成为一个非常重要的环节。测试的目的是发现空间中的缺陷,以确保软件的质量和稳定性。然而,缺陷密度的高低是影响测试效果的重要因素之一。高缺陷密度意味着测试人员需要更多的时间和精…

Mysql——SQL语言入门

1.创建数据库表 【1】创建数据库表t_student (1)创建数据库: (2)新建查询 (3)创建数据库表 ##单行注释 /* 多行注释 多行注释 *//* 建立一张用来存储学生信息的表 字段包含学号、姓名、性别、…

Golang笔记:使用melody包进行WebSocket通讯

文章目录 目的使用示例与说明总结 目的 WebSocket是Web开发应用中非常常用的功能,用于客户端和服务器间长时间的实时双向数据通讯。Golang中官方并没有实现这个功能,需要借助第三方的包来实现。 目前被最广泛使用的包是 gorilla/websocket https://pkg…

环信 uni-app-demo 升级改造计划——整体代码重构优化(二)

概述 本次关于 uni-app 代码整体重构工作,基于上一期针对 uni-app 官网 demo 从 vue2 迁移 vue3 框架衍生而来,在迁移过程中有明显感知,目前的项目存在的问题为,项目部分代码风格较为不统一,命名不够规范,注…

Spring boot 注解@Async不生效 无效 不起作用

今天在做公司项目时,有一个发邮件的需求。所以写了一个发送邮件的方法后来发现发邮件很慢,导致接口响应也很慢。于是我便想到要使用异步调用去处理这个方法。于是我把注解Async 加到了自己service类下的一个发邮件的一个方法,后来发现并没有生…

人工智能之数据增广详解

人工智能(AI)中的数据增广技术,是通过对已有数据进行特定变换,从而生成新的训练数据的方法。数据增广技术往往可以显著提高模型的性能,从而在许多应用场景中获得更好的分类、检测、识别等结果。本文将对数据增广技术进…

Moonbeam近日提案公投一览

正在跟进Moonbeam治理的小伙伴,一起来快速浏览一下近期生态中正在发生的事情吧!其中包含多个去中心化应用的Grant加速计划提案、HRMP开拓提案以及优化质押相关平台的内容。许多提案都与网络的运作息息相关,一起了解和参与Moonbeam的发展吧&am…