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

news2024/12/24 1:04:39

目录

  • 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]。

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

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与我们之前计算的一样。但是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()函数来寻找直方图。请看下面的代码。

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图很有帮助。为此,你需要先找到直方图的数据。试试下面的代码。

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()来寻找全图的直方图。如果你想找到图像中某些区域的直方图呢?只要在你想找直方图的区域创建一个白色的蒙版图像,其他区域设置为黑色。然后把它作为掩膜传给你。

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函数。

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关于掩膜数组的文档中读到更多关于它的信息。

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(你来做),结果看起来像下面。

Image Name

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

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)

Image Name

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

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

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

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

Image Name

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

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

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

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

Image Name

其他资源

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

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

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

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

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

相关文章

【笔试强训选择题】Day16.习题(错题)解析

作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!! 文章目录…

Linux Qt6 安装教程及错误解决

在Linux环境,通常为Ubuntu,安装Qt开发环境,与Windows安装相比,还是稍显繁琐,需要多做几个步骤。 这里的Ubuntu版本采用的是ubuntu-22.04.2-desktop-amd64,所以,比旧版本会少很多坑,…

今天面了个字节跳动拿35K出来的测试,真是砂纸擦屁股,给我露了一手啊

今年的春招已经结束,很多小伙伴收获不错,拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文,为此咱这里也统一做一次大整理和大归类,这也算是划重点了。 俗话说得好,他山之石…

现在的00后,实在是太卷了,我们这些老油条都想辞职了......

现在的小年轻真的卷得过分了。前段时间我们公司来了个00年的,工作没两年,跳槽到我们公司起薪20K,都快要超过我了。 后来才知道人家是个卷王,从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天,原来这位小老弟家…

MathType7公式编辑器新版详细介绍下载安装

由于CSDN这边不能发相关的教程等,若仅用于学习体验,请移步,有能力请支持正版。 wx供重浩:创享日记 对话框发送:mathtype 免费获取MathType-win-zh.exe安装包 它是一款用于数学公式编辑和排版的软件。MathType可以在Mi…

能源革命:可持续能源技术如何改变世界

随着全球气候变化日趋严重,能源转型成为解决气候问题和提高全球能源安全合理性的必要措施之一。可持续能源技术因其对环境的友好性和可再生性而成为了当前热点话题。你认为可持续能源技术真的能改变世界吗?一起来说说你的看法吧! 一、你在工…

实现能效升级 | 基于ACM32 MCU的冰箱压缩机变频方案

概述 冰箱制冷系统中最重要的部件是压缩机。它从吸气管吸入低温低压的制冷剂气体,通过电机运转带动活塞对其进行压缩后,向排气管排出高温高压的制冷剂气体,为整个制冷循环提供源动力。这样就实现了压缩→冷凝→膨胀→蒸发 ( 吸热 ) 的制冷循…

1016. 子串能表示从 1 到 N 数字的二进制串

给定一个二进制字符串 s 和一个正整数 n,如果对于 [1, n] 范围内的每个整数,其二进制表示都是 s 的 子字符串 ,就返回 true,否则返回 false 。 子字符串 是字符串中连续的字符序列。 示例 1: 输入:s &qu…

【C++】new和delete

new是个运算符 使用: new 类型(初始值); malloc和new的区别: 1--new申请空间失败抛出异常,malloc返回空指针 ip(new(nothrow) Int(10))//不想它抛出异常 2--new调用构造函数 3--new可以重…

智慧闹钟

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 首先相对于传闹钟设置闹铃的方式,我想把这款创意闹钟设计成通过光照传感器来实现对闹钟的开启,更有效实现闹钟的自动化、智能化,不过为了防止误触带来闹钟的开启,需要对开启条件…

一起搭建我的世界服务器来实现公网远程联机教程「不需要公网IP」

文章目录 前言1. 安装JAVA2. MCSManager安装3.局域网访问MCSM4.创建我的世界服务器5.局域网联机测试6.安装cpolar内网穿透7. 配置公网访问地址8.远程联机测试9. 配置固定远程联机端口地址9.1 保留一个固定tcp地址9.2 配置固定公网TCP地址9.3 使用固定公网地址远程联机 转载自内…

第十七章_Redis布隆过滤器BloomFilter实战

是什么 一句话 由一个初值都为零的bit数组和多个哈希函数构成,用来快速判断集合中是否存在某个元素 设计思想 目的 减少内存占用 方式 不保存数据信息,只是在内存中做一个是否存在的标记flag 本质就是判断具体数据是否存在于一个大的集合中 备注 布…

好兄弟单身?这不得用python来帮他脱离苦海

明天什么节日 ?明天谁过节 ? 是你吗,还是你的朋友 ?如果是你的话,那咱就帮帮朋友,到年龄的咱就直接相亲呗 赠人玫瑰 手留余香 好人做到底,来让朋友体验体验恋爱的感觉~ 今天就带你们来爬爬相亲…

BoostSearch搜索引擎

今天讲的项目是基于C的Boost库的站内搜索引擎。因为Boost库内没有搜索关键字功能,所以在这里我们来手动实现一个这样的搜索引擎。当用户在输入框输入要查询的关键字后,就会快速查询出相关的 boost 库中的文档,弥补 boost 在线文档没有搜索功能…

C++进阶——mapset的使用

C进阶——map&set的使用 关联式容器 在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C11)等,这 些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存…

chatgpt赋能Python-python2的阶乘

Python2中的阶乘 Python是一种高级编程语言,它可以用来编写各种各样的程序。在Python中,阶乘是一个经常用到的数学概念,由于其强大的计算能力和易学易用的特点,Python成为了计算阶乘的一个很好的工具。在这篇文章中,我…

MySql 数据库的锁机制和原理

MySQL是一种流行的关系型数据库管理系统,广泛应用于各种Web应用程序和企业级应用程序中。在MySQL中,锁是一种用于控制并发访问的机制,它可以保证数据的一致性和完整性。本文将介绍MySQL的锁机制及原理,包括锁的类型、级别和实现原…

Idea配置moven

moven的下载与安装 https://maven.apache.org/download.cgi 解压到指定位置,配置环境变量 编辑系统变量Path,添加变量值:%MAVEN_HOME%\bin winr输入cmd输入 mvn -v 出现上述界面,则表示成功安装Maven 新建一个文件夹作为本地…

[入门必看]数据结构5.5:树与二叉树的应用

[入门必看]数据结构5.5:树与二叉树的应用 第五章 树与二叉树5.5 树与二叉树的应用知识总览5.5.1 哈夫曼树5.5.2_1 并查集5.5.2_2 并查集的进一步优化 5.5.1 哈夫曼树带权路径长度哈夫曼树的定义哈夫曼树的构造哈夫曼编码应用:英文字母频次 5.5.2_1 并查集…

MybatisPlus详解

文章目录 1.MyBatisPlus的介绍1.1 MybatisPlus的特性讲解1.2 支持的数据库1.3 MybatisPlus的日志 2.映射2.1 自动映射规则2.2 表映射2.3 字段映射2.4 字段失效2.5 视图属性 3. 条件构造器3.1 等值查询3.1.1 eq3.1.2 allEq3.1.3 ne 不等于 3.2 范围查询3.2.1 gt 大于3.2.2 ge 大…