当前位置: 首页 > news >正文

OpenCV-Python (官方)中文教程(部分一)_Day20

22.直方图

22.1直方图的计算,绘制与分析

使用 OpenCV 或 Numpy 函数计算直方图

使用 Opencv 或者 Matplotlib 函数绘制直方图

将要学习的函数有:cv2.calcHist(),np.histogram()

什么是直方图呢?通过直方图你可以对整幅图像的灰度分布有一个整体的 了解。直方图的 x 轴是灰度值(0 到 255),y 轴是图片中具有同一个灰度值的 点的数目。

直方图其实就是对图像的另一种解释。一下图为例,通过直方图我们可以 对图像的对比度,亮度,灰度分布等有一个直观的认识。几乎所有的图像处理 软件都提供了直方图分析功能。下图来自Cambridge in Color website,强 烈推荐你到这个网站了解更多知识。

让我们来一起看看这幅图片和它的直方图吧。(要记住,直方图是根据灰度 图像绘制的,而不是彩色图像)。直方图的左边区域像是了暗一点的像素数量, 右侧显示了亮一点的像素的数量。从这幅图上你可以看到灰暗的区域比两的区 域要大,而处于中间部分的像素点很少。

(1).统计直方图

现在我们知道什么是直方图了, 那怎样获得一副图像的直方图呢? OpenCV和Numpy 都有内置函数做这件事。在使用这些函数之前我们有 必要想了解一下直方图相关的术语。

BINS上面的直方图显示了每个灰度值对应的像素数。如果像素值为 0 到 255,你就需要 256 个数来显示上面的直方图。但是,如果你不需要知道每一个像素值的像素点数目的,而只希望知道两个像素值之间的像素点数目怎么办呢?举例来说,我们想知道像素值在 0 到 15 之间的像素点的数目,接着 是 16 到 31,....,240 到 255。我们只需要 16 个值来绘制直方图。OpenCV Tutorials   on  histograms中例子所演示的内容。

那到底怎么做呢?你只需要把原来的 256 个值等分成 16 小组,取每组的总和。而这里的每一个小组就被成为 BIN。第一个例子中有 256 个 BIN,第 二个例子中有 16个 BIN。在 OpenCV  的文档中用 histSize  表示   BINS。

DIMS表示我们收集数据的参数数目。在本例中,我们对收集到的数据只考虑一件事:灰度值。所以这里就是 1。

RANGE:就是要统计的灰度值范围,一般来说为 [0,256],也就是说所有的灰度值

使用 OpenCV 统计直方图 函数 cv2.calcHist 可以帮助我们统计一幅图像的直方图。我们一起来熟悉一下这个函数和它的参数:

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

  1. images: 原图像(图像格式为 uint8 或 float32)。当传入函数时应该 用中括号 [] 括起来,例如:[img]。

  2. channels: 同样需要用中括号括起来,它会告诉函数我们要统计那幅图 像的直方图。如果输入图像是灰度图,它的值就是 [0];如果是彩色图像 的话,传入的参数可以是  [0],[1],[2]   它们分别对应着通道  B,G,R。

  3. mask: 掩模图像。要统计整幅图像的直方图就把它设为 None。但是如 果你想统计图像某一部分的直方图的话,你就需要制作一个掩模图像,并 使用它。(后边有例子)

  4. histSize:BIN  的数目。也应该用中括号括起来,例如:[256]。

  5. ranges: 像素值范围,通常为 [0,256]

让我们从一副简单图像开始吧。以灰度格式加载一幅图像并统计图像的直方图。

import cv2

import numpy as np

import matplotlib.pyplot as plt

# 1. 读取图像(灰度模式)

img = cv2.imread('home.jpg', 0)  # 参数0表示以灰度模式读取

# 检查图像是否成功加载

if img is None:

    raise FileNotFoundError("无法加载图像,请检查路径")

# 2. 计算直方图

hist = cv2.calcHist(

    [img],       # 输入图像(必须用列表包裹)

    [0],         # 通道索引(灰度图是0)

    None,        # 掩膜(None表示整个图像)

    [256],       # 直方图大小(bin的数量)

    [0, 256]     # 像素值范围

)

# 3. 可视化直方图

plt.figure(figsize=(10, 5))

plt.title("Grayscale Histogram")

plt.xlabel("Pixel Value")

plt.ylabel("Frequency")

plt.xlim([0, 256])  # X轴范围

plt.plot(hist, color='black')

plt.grid(True, linestyle='--', alpha=0.7)

plt.show()

# 4. 可选:显示原图

cv2.imshow("Original Image", img)

cv2.waitKey(0)

cv2.destroyAllWindows()

hist   是一个  256x1  的数组,每一个值代表了与次灰度值对应的像素点数目。

使用 Numpy 统计直方图 Numpy 中的函数 np.histogram() 也可以帮 我们统计直方图。你也可以尝试一下下面的代码:

import cv2

import numpy as np

import matplotlib.pyplot as plt

# 1. 读取图像(灰度模式)

img = cv2.imread('11111.png', 0)  # 参数0表示以灰度模式读取

# 检查图像是否成功加载

if img is None:

    raise FileNotFoundError("无法加载图像,请检查路径")

# 2. 使用numpy计算直方图(不需要中括号)

hist, bins = np.histogram(img.ravel(),  # 将图像展平为一维数组

                         bins=256,      # 直方图的bin数量

                         range=[0, 256]) # 像素值范围

# 3. 可视化直方图

plt.figure(figsize=(10, 5))

plt.title("Grayscale Histogram (numpy)")

plt.xlabel("Pixel Value")

plt.ylabel("Frequency")

plt.xlim([0, 256])  # X轴范围

# 注意:bins比hist多一个元素,需要调整显示

plt.bar(bins[:-1],        # X轴坐标(bin的左边界)取bins数组除最后一个元素外的所有值(因为bins比hist多一个元素)

        hist,             # Y轴高度(频数)每个bin对应的像素频数

        width=1,          # 柱子的宽度 设置每个柱子的宽度为1(保证柱子之间无间隙)

        color='black',    # 柱子填充颜色 黑色

        edgecolor='none') # 柱子边框颜色(无边框)

plt.grid(True,           # 启用网格线

         linestyle='--', # 虚线样式

         alpha=0.7)      # 透明度(0.7表示70%不透明)

plt.show()

# 4. 可选:显示原图

cv2.imshow("Original Image", img)

cv2.waitKey(0)

cv2.destroyAllWindows()

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.histgram   的 十 倍。 所 以 对 于 一 维 直 方 图, 我 们 最 好 使 用 这 个 函 数。 使 用   np.bincount   时 别 忘 了 设 置   minlength=256。 例 如, hist=np.bincount(img.ravel(),minlength=256)

注 意:OpenCV的函数要比 np.histgram()快40倍.所以坚持使用OpenCV 函数.

(2).绘制直方图

有两种方法来绘制直方图:

1.Short Way(简单方法):使用 Matplotlib 中的绘图函数。

2.Long Way(复杂方法):使用 OpenCV 绘图函数

使用 Matplotlib 中有直方图绘制函数:matplotlib.pyplot.hist()

它可以直接统计并绘制直方图。你应该使用函数  calcHist()  或 np.histogram()

统计直方图。

以下是关于 matplotlib.pyplot.hist()、cv2.calcHist() 和 np.histogram() 三个函数的详细对比分析,包括它们的区别、优缺点和适用场景:

1. matplotlib.pyplot.hist()​

特点:

​​一体化操作:直接统计并绘制直方图(计算+可视化一步完成)

​​接口简单:无需手动处理 bins 和 hist 的对应关系

​​可视化集成:自动添加坐标轴、标题等图形元素

import cv2

from matplotlib import pyplot as plt

 

img = cv2.imread('drawing.png',0) 

plt.hist(img.ravel(),256,[0,256])

plt.show()

2. cv2.calcHist()​​

特点:

​​OpenCV专属:针对图像数据高度优化

​​多通道支持:可同时计算多通道直方图

​​灵活掩膜:支持指定ROI区域计算

import cv2

img = cv2.imread('image.jpg', 0)

hist = cv2.calcHist([img], [0], None, [256], [0, 256])

# 需要手动绘制

plt.plot(hist, color='red')

plt.title('OpenCV Histogram')

plt.show()

3. np.histogram()​​

特点:

​​纯计算函数:只返回统计结果,不包含绘图

​​通用性强:适用于任何数值数据(不限于图像)

​​精确控制:可自定义 bins 和 range

import numpy as np

import matplotlib.pyplot as plt

img = cv2.imread('image.jpg', 0)

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

# 手动绘制(需处理bins边界)

plt.bar(bins[:-1], hist, width=1)

plt.title('NumPy Histogram')

plt.show()

如何选择?​

​​1、需要快速可视化​​ → plt.hist()

plt.hist(img.ravel(), bins=256, range=[0,256], alpha=0.7)

​​2、OpenCV图像处理流程​​ → cv2.calcHist()

hist = cv2.calcHist([img], [0], mask, [256], [0,256])

​​3、精确控制或非图像数据​​ → np.histogram()

hist, bins = np.histogram(data, bins=50, range=[min_val, max_val])

4、多通道图像分析​​ → cv2.calcHist() + 循环

colors = ('b','g','r')

for i, col in enumerate(colors):

    hist = cv2.calcHist([img], [i], None, [256], [0,256])

plt.plot(hist, color=col)

性能实测对比​

import time

import matplotlib.pyplot as plt

import cv2

import numpy as np

img = cv2.imread('drawing.png', 0)

# 测试plt.hist

t1 = time.time()

plt.hist(img.ravel(), bins=256)

print(f"plt.hist: {time.time()-t1:.4f}s")

# 测试cv2.calcHist

t2 = time.time()

hist = cv2.calcHist([img], [0], None, [256], [0,256])

print(f"cv2.calcHist: {time.time()-t2:.4f}s")

# 测试np.histogram

t3 = time.time()

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

print(f"np.histogram: {time.time()-t3:.4f}s")

典型输出(4000x3000像素图像):

plt.hist: 3.7026s

cv2.calcHist: 0.0060s

np.histogram: 0.0209s

或者你可以只使用 matplotlib 的绘图功能,这在同时绘制多通道(BGR) 的直方图,很有用。但是要告诉绘图函数你的直方图数据在哪里。运行 一下下面的代码:

import cv2

from matplotlib import pyplot as plt

img = cv2.imread('drawing.png') #读取BGR格式的彩色图像(默认加载为3通道numpy数组)

if img is None:

    raise FileNotFoundError("无法加载图像,请检查路径")

#元组存储三个颜色符号,对应Matplotlib的绘图颜色:

#'b': 蓝色(Blue,OpenCV的通道0)

#'g': 绿色(Green,通道1)

#'r': 红色(Red,通道2)

color = ('b', 'g', 'r')

#同时获取索引i(0,1,2)和颜色符号col('b','g','r')

for i, col in enumerate(color):

    #[img]: 输入图像(必须放在列表中)[i]: 通道索引(0=Blue, 1=Green, 2=Red)

    #None: 不使用掩膜(计算全图)[256]: 直方图bin数量(256级)[0,256]: 像素值范围(0~255)

    histr = cv2.calcHist([img], [i], None, [256], [0,256])

    #histr: 当前通道的直方图数据(256维向量)color=col: 使用对应通道的颜色(蓝/绿/红)

    plt.plot(histr, color=col, label=f'{col.upper()} Channel')  # 添加标签

plt.title('RGB Channel Histogram')

plt.xlabel('Pixel Value')

plt.ylabel('Frequency')

plt.xlim([0, 256]) #限制X轴显示范围为0~256(覆盖所有像素值)

plt.legend()  # 显示图例

plt.grid(True, linestyle='--', alpha=0.5)  # 添加网格线

plt.show()

输出效果​​

将显示一个包含三条曲线的直方图:

​​蓝色曲线​​: 蓝色通道的像素值分布

​​绿色曲线​​: 绿色通道的分布

​​红色曲线​​: 红色通道的分布

(X轴为像素值0~255,Y轴为对应像素值的出现频率)

关键点总结​​

​​enumerate(): 同时获取索引和值,避免手动计数

​​calcHist()的列表输入: 必须用[img]和[i]传递参数

​​通道顺序: OpenCV默认BGR,Matplotlib默认RGB,注意颜色对应关系

使用 OpenCV 自带函数绘制直方图比较麻烦,这里不作介 绍,有兴趣可以自己研究。可以参考 OpenCV-Python2 的官方示例。

(3).使用掩模

要统计图像某个局部区域的直方图只需构建一副掩模图像。将要统计的部分设置成白色,其余部分为黑色,就构成一副掩模图像。然后把这个掩模图像传给函数就可以了。

import matplotlib.pyplot as plt

import cv2

import numpy as np

img = cv2.imread('drawing.png',0)

 

# create a mask

#创建与图像尺寸相同的全黑掩膜(所有像素值为0)img.shape[:2]获取图像的高度和宽度(忽略通道数)

mask = np.zeros(img.shape[:2], np.uint8) 

#将掩膜中y=100:300,x=100:400的矩形区域设为白色(255),该区域表示后续要分析的ROI

mask[100:300, 100:400] = 255

#对图像进行按位与操作,保留掩膜白色区域对应的原图像素

#参数说明:前两个img:输入图像(灰度图),mask=mask:指定掩膜区域

#效果:非ROI区域变为黑色(0),ROI区域保留原灰度值

masked_img = cv2.bitwise_and(img,img,mask = mask)

# Calculate histogram with mask and without mask

# Check third argument for mask

#计算全局和局部直方图

hist_full = cv2.calcHist([img],[0],None,[256],[0,256])

hist_mask = cv2.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')

#默认先画的hist_full为蓝色,后画的hist_mask为橙色

plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask) 

plt.xlim([0,256])

 

plt.show()

结果如下,其中蓝线是整幅图像的直方图,橙色线是进行掩模之后的直方图。

相关文章:

  • 软件架构选型之“如何选”
  • 生物化学笔记:神经生物学概论05 感受野 视觉中枢 高级视皮层中的信息走向
  • 【python实用小脚本-43】用Python自动发送生日祝福,让情感更高效
  • 希尔伯特第十问题:是一个伪命题
  • 时态--00--总述
  • 代码随想录打卡|Day31动态规划(最后一块石头的重量2、目标和、一和零)
  • 如何在 Linux 环境下使用 Certbot 自动生成 SSL 证书并部署到 Nginx 服务中
  • POSIX介绍
  • SD - WAN 跨境网络专线部署方式介绍
  • 国产工业软件突破路径
  • 电脑干货:开源免费的QQ空间说说下载工具GetQzonehistory
  • 机器指标监控技术方案
  • FreeBSD升级到14.2后启动的时候提示BOOT LOADER IS TOO OLD. PLEASE UPGRADE.
  • 利用v0与Cursor优化开发流程和效率
  • 六.割草机技术总结--6.RTK定位精度分析
  • Arduino程序结构详解与嵌入式开发对比指南
  • 7.软考高项(信息系统项目管理师)-资源管理
  • 中国1km分辨率1901-2023年均气温降水数据
  • DotNet 入门:(二) 项目运行
  • 分组密码算法ShengLooog设计原理详解
  • 北京亦庄启动青年人才创新创业生态示范区
  • 屠呦呦当选美国国家科学院外籍院士
  • 孕妇乘坐高铁突发临产,广西铁路部门协助送医平安产子
  • 王毅:时代不容倒退,公道自在人心
  • 辽宁辽阳火灾3名伤者无生命危险
  • 日趋活跃!2024年我国数据生产总量同比增长25%