【OpenCV】直方图

理论

可以将直方图视为图形或曲线图,从而使您对图像的强度分布有一个整体的了解。它是在X轴上具有像素值(不总是从0到255的范围),在Y轴上具有图像中相应像素数的图。

这只是理解图像的另一种方式。通过查看图像的直方图,您可以直观地了解该图像的对比度,亮度,强度分布等。当今几乎所有图像处理工具都提供直方图功能。

histogram_sample.jpg

可以看到图像及其直方图。(请记住,此直方图是针对灰度图像而非彩色图像绘制的)。直方图的左侧区域显示图像中较暗像素的数量,而右侧区域则显示较亮像素的数量。从直方图中,您可以看到暗区域多于亮区域,中间值的数量(中间值的像素值,例如127附近)非常少。

查找直方图

现在我们有了一个关于直方图的想法,我们可以研究如何找到它。OpenCV和Numpy都为此内置了功能。在使用这些功能之前,我们需要了解一些与直方图有关的术语。

BINS :上面的直方图显示每个像素值的像素数,即从0到255。即,您需要256个值来显示上面的直方图。但是考虑一下,如果您不需要分别找到所有像素值的像素数,而是找到像素值间隔中的像素数怎么办?例如,您需要找到介于0到15之间,然后16到31之间,...,240到255之间的像素数。您只需要16个值即可表示直方图。这就是在 OpenCV直方图教程 中给出的示例中所显示的内容。

因此,您要做的就是将整个直方图分成16个子部分,每个子部分的值就是其中所有像素数的总和。每个子部分都称为“BIN”。在第一种情况下,bin的数量为256个(每个像素一个),而在第二种情况下,bin的数量仅为16个。BINS由OpenCV文档中的 histSize 术语表示。

DIMS :这是我们为其收集数据的参数的数量。在这种情况下,我们仅收集关于强度值的一件事的数据。所以这里是1。

范围 :这是您要测量的强度值的范围。通常,它是[0,256],即所有强度值。

1. OpenCV中的直方图计算

因此,现在我们使用 cv.calcHist() 函数查找直方图。让我们熟悉一下函数及其参数: $$ cv.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]]) $$

  1. images:它是uint8或float32类型的源图像。它应该放在方括号中,即“ [img]”。
  2. channels:也以方括号给出。它是我们计算直方图的通道的索引。例如,如果输入为灰度图像,则其值为[0]。对于彩色图像,您可以传递[0],[1]或[2]分别计算蓝色,绿色或红色通道的直方图。
  3. mask:遮罩图像。为了找到完整图像的直方图,将其指定为“无”。但是,如果要查找图像特定区域的直方图,则必须为此创建一个遮罩图像并将其作为遮罩。(我将在后面显示一个示例。)
  4. histSize:这表示我们的BIN计数。需要放在方括号中。对于全尺寸,我们通过[256]。
  5. ranges:这是我们的RANGE。通常为[0,256]。 因此,让我们从示例图像开始。只需在灰度模式下加载图像并找到其完整的直方图即可。
img = cv.imread('home.jpg',0)
hist = cv.calcHist([img],[0],None,[256],[0,256])

hist是256x1的数组,每个值对应于该图像中具有相应像素值的像素数。

2. Numpy中的直方图计算

Numpy还为您提供了一个函数 np.histogram() 。因此,您可以在下面的行尝试代替 calcHist() 函数:

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

hist与我们之前计算的相同。但是bin将具有257个元素,因为Numpy计算出bin的范围为0-0.99、1-1.99、2-2.99等。因此最终范围为255-255.99。为了表示这一点,他们还在料箱末端添加了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() 函数来查找直方图。请参见下面的代码:

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()

如下图:

histogram_matplotlib.jpg

或者,可以使用matplotlib的法线图,这对于BGR图是很好的。为此,您需要首先找到直方图数据。试试下面的代码:

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()

结果:

histogram_rgb_plot.jpg

蓝色在图像中具有一些高值区域(显然这应该是由于天空)

2.使用OpenCV

好吧,在这里您可以调整直方图的值及其bin值,使其看起来像x,y坐标,以便可以使用 cv.line() 或 cv.polyline() 函数绘制它以生成与上述相同的图像。OpenCV-Python2官方示例已经提供了此功能。检查 samples/python/hist.py 代码

遮罩的应用

我们使用 cv.calcHist() 查找完整图像的直方图。如果要查找图像某些区域的直方图怎么办?只需在要查找直方图的区域上创建白色的蒙版图像,否则创建黑色。然后通过这个作为面具。

img = cv.imread('home.jpg',0)
# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv.bitwise_and(img,img,mask = mask)
# Calculate histogram with mask and without mask
# Check third argument for 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, 'gray')
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()

查看结果。在直方图中,蓝线表示完整图像的直方图,绿线表示遮蔽区域的直方图。

histogram_masking.jpg

直方图均衡

我们将学习直方图均衡化的概念,并将其用于改善图像的对比度。 理论 考虑一个图像,其像素值仅限于特定的值范围。例如,较亮的图像会将所有像素限制在较高的值。但是,好的图像将具有来自图像所有区域的像素。因此,您需要将此直方图拉伸到两端(如下图所示,来自维基百科),这就是直方图均衡化的作用(简单来说)。通常,这可以提高图像的对比度。

histogram_equalization.png

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()

histeq_numpy1.jpg

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

现在,我们找到最小的直方图值(不包括0)并应用Wiki页面中给出的直方图均衡方程。但是我在这里使用了Numpy的masked array概念数组。对于掩码数组,所有操作都在非掩码元素上执行。您可以从有关屏蔽数组的Numpy文档中了解有关此内容的更多信息。

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

现在我们有了查找表,该表为我们提供了有关每个输入像素值的输出像素值是什么的信息。因此,我们仅应用变换。

img2 = cdf[img]

现在我们像以前一样计算它的直方图和cdf(您这样做),结果如下所示:

histeq_numpy2.jpg

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

OpenCV中的直方图均衡

OpenCV具有执行此操作的功能 cv.equalizeHist() 。它的输入只是灰度图像,输出是我们的直方图均衡图像。

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

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)

equalization_opencv.jpg

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

当图像的直方图限制在特定区域时,直方图均衡化效果很好。在直方图覆盖较大区域(即同时存在亮像素和暗像素)的强度变化较大的地方,效果不好。

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

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

clahe_1.jpg

直方图均衡后,背景对比度确实得到了改善。但是在两个图像中比较雕像的脸。由于亮度过高,我们在那里丢失了大多数信息。这是因为它的直方图不像我们在前面的案例中所看到的那样局限于特定区域(尝试绘制输入图像的直方图,您将获得更多的直觉)。

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

下面的代码片段显示了如何在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)

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

clahe_2.jpg

apachecn.github.io/opencv-doc-zh/#/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/63768.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【PlantUML系列】用例图(三)

目录 一、组成部分 二、典型案例 一、组成部分 参与者(Actors):使用关键字 actor 后跟参与者的名称。用例(Use Cases):使用关键字 usecase 后跟用例的名称和编号(可选)。系统边界…

Transformer部分知识点解释

传统Transformer 经典QKV算法 Transformer架构的优势与问题 万能模型,直接套用,代码实现简单,现成例子一大片并行的,比LSTM快,全局信息丰富,注意力机制效果好长序列中attention需要每一个点跟其他点计算(…

聚类及Python下实现 K-means 算法

聚类 聚类是无监督学习中的一种重要方法,旨在将数据集中相似的数据对象划分到同一个簇中,使得不同簇之间的数据对象差异尽可能大。在大数据环境下,聚类可以帮助挖掘数据中的隐藏结构和模式,应用场景十分广泛,比如在客…

LabVIEW调用Thorlabs的动态库进行开发

Thorlabs 产品在科研与生产领域中的应用广泛,当需要基于LabVIEW 进行二次开发时,可按照以下方法操作,以充分发挥设备性能并满足特定的项目需求。 创建 Kinesis LabVIEW 项目文件和文件夹 更详细的说明参见附件 在 LabVIEW 的启动界面中选择…

三、Zookeeper

Zookeeper 三、Zookeeper3.1什么是zookeeper?3.2为什么需要zookeeper3.3Zookeeper基本运行流程3.4Zookeeper数据模型3.5Zookeeper主要角色3.6Zookeeper工作原理3.7Zookeeper节点数据操作流程三、Zookeeper 3.1什么是zookeeper? ZooKeeper是一个分布式的,开放源码的分布式应…

实现盘盈单自动化处理:吉客云与金蝶云星空数据对接

盘盈单103v2对接其他入库:吉客云数据集成到金蝶云星空 在企业信息化管理中,数据的高效流转和准确性至关重要。本文将分享一个实际案例,展示如何通过轻易云数据集成平台,将吉客云的数据无缝对接到金蝶云星空,实现盘盈单…

vue:构造结构,塞入数据:结构不变,数据改变

1. 引入vue 2. 创建页面容器,让vue去管理这个容器 3. 双向绑定 4. 单向绑定 5. 操作容器(数据模型)里的数据 5. 单向绑定的简写 6. v-if和v-else 6. v-if和v-show的区别: v-if要重新渲染界面,v-show只需要改一下元素属…

CountDownLatch阻塞后countDown未执行会如何?

背景 某项目封装了 Kafka 消费者 API,根据传递的消费者线程数,创建 N 个消费者线程同时消费对应 topic 的数据,并在线程启动后收集到全局列表中,方便在程序调用 stop 流程时逐个停止。 主控类在创建 Kafka 消费线程时使用了 Cou…

NLP与LLM的工程化实践与学习思考 - 说说知识图谱

NLP与LLM的工程化实践与学习思考[24年半年工作总结] - 说说知识图谱 0 真的就是先说说1 为什么知识图谱什么是知识图谱?基于图的数据结构?基于数据结构的图?知识图谱的技术要点两个技术维度:知识、图七个技术要点:表示…

【CVE-2024-5660】ARM CPU漏洞:硬件页面聚合(HPA)安全通告

安全之安全(security)博客目录导读 目录 一、概述 二、修改历史 三、什么是硬件页面聚合? 四、修复解决 一、概述 在一些基于arm的cpu中发现了一个问题,该问题可能允许修改的、不受信任的客户机操作系统(guest OS)危及某些H…

项目中使用AntV L7地图(五)添加飞线

项目中使用AntV L7地图,添加 飞线 文档地址:https://l7.antv.antgroup.com/zh/examples/line/animate/#trip_animate 一、初始化地图 使用的地图文件为四川地图JSON,下载地址:https://datav.aliyun.com/portal/school/atlas/area_selector#&…

《九重紫》逐集分析鉴赏—序言、概览、框架分析

主标题:《九重紫》一起追剧吧副标题:《九重紫》逐集分析鉴赏—序言、概览、框架分析《永夜星河》后,以为要浅尝剧荒,一部《九重紫》突出重围。 看了宣传片感觉不是很差,看了部分剪辑感觉还可以,看了一两集感…

生成式AI概览与详解

1. 生成式AI概览:什么是大模型,大模型应用场景(文生文,多模态) 生成式AI(Generative AI)是指通过机器学习模型生成新的数据或内容的人工智能技术。生成式AI可以生成文本、图像、音频、视频等多种…

0001.简易酒店管理系统后台

一.系统架构 springmvcjsplayuimysql 二.功能特性 简单易学习,虽然版本比较老但是部署方便,tomcat环境即可启用;代码简洁,前后端代码提供可统一学习;祝愿您能成尽快为一位合格的程序员,愿世界没有BUG; …

STM32软件IIC驱动TCA9548A多路测量AHT10

STM32软件IIC驱动TCA9548多路测量AHT10 TCA9548AAHT10代码逻辑代码展示现象总结 TCA9548A TCA9548A 有八个可通过 I2C 总线控制的双向转换开关,SCL/SDA 上行对扩展到八个下行对,或者通道,适用于系统中存在I2C目标地址冲突的情况。8路双向转换…

信奥赛CSP-J复赛集训(dfs专题)(15):洛谷P8838:[传智杯 #3 决赛] 面试

信奥赛CSP-J复赛集训(dfs专题-刷题题单及题解)(15):洛谷P8838:[传智杯 #3 决赛] 面试 题目背景 disangan233 和 disangan333 去面试了,面试官给了一个问题,热心的你能帮帮他们吗? 题目描述 现在有 n n n 个服务器,服务器

ARM学习(36)静态扫描规则学习以及工具使用

笔者来学习了解一下静态扫描以及其规则,并且亲身是实践一下对arm 架构的代码进行扫描。 1、静态扫描认识 静态扫描:对代码源文件按照一定的规则进行扫描,来发现一些潜在的问题或者风险,因为不涉及代码运行,所以其一般…

Python机器学习笔记(五、决策树集成)

集成(ensemble)是合并多个机器学习模型来构建更强大模型的方法。这里主要学习两种集成模型:一是随机森林(random forest);二是梯度提升决策树(gradient boosted decision tree)。 1…

为什么数据平台需要敏捷版|直播回顾

11月28日,我们邀请到StartDT合伙人、CTO地雷和StartDT资深战略咨询专家何夕,围绕“为什么数据平台需要敏捷版”这个话题,向大家汇报了DataSimba敏捷版这半年来的最新进展,并带来了详细的产品解读。 敏捷版支持StarRocks、ClickHo…

SpringBoot【九】mybatis-plus之自定义sql零基础教学!

一、前言🔥 环境说明:Windows10 Idea2021.3.2 Jdk1.8 SpringBoot 2.3.1.RELEASE mybatis-plus的基本使用,前两期基本讲的差不多,够日常使用,但是有的小伙伴可能就会抱怨了,若是遇到业务逻辑比较复杂的sq…