OpenCV:图像直方图

目录

一、什么是图像直方图?

关键概念:BINS(区间)

二、直方图的核心作用

三、OpenCV 计算直方图:calcHist 函数详解

1. 函数语法与参数解析

2. 基础实战:计算灰度图直方图

代码实现

结果分析

3. 进阶实战:计算彩色图直方图

代码实现

结果分析

4. 高级实战:用掩模(mask)计算局部直方图

代码实现

关键说明

四、直方图均衡化:提升图像对比度

1. OpenCV 实现直方图均衡化

代码实现

结果分析

五、常见问题与解决方案


在计算机视觉领域,图像直方图是分析图像像素分布的核心工具,无论是图像增强、对比度调整,还是目标检测中的特征提取,都离不开它的身影。本文将从直方图的基本概念入手,结合数学原理与 OpenCV 代码实例,带你全面掌握图像直方图的理论与实践,最后还会拓展直方图均衡化等进阶应用,助力你解决实际项目中的问题。


一、什么是图像直方图?

图像直方图是图像像素灰度级别分布的图形化表达,它以 “像素灰度值” 为横轴,以 “该灰度值对应的像素数量” 为纵轴,直观地呈现图像的明暗分布特征。

举个简单例子:一张纯黑图像的直方图,只会在灰度值 0 的位置出现一个峰值;而一张高对比度图像的直方图,像素会集中在灰度值较低(暗部)和较高(亮部)的区域,中间灰度区域像素较少。

关键概念:BINS(区间)

默认情况下,直方图会统计 0-255 每个灰度值的像素数量,此时需要 256 个 “区间” 来展示,这 256 个区间就称为BINS(对应 OpenCV 中calcHist函数的histSize参数)。

但在实际场景中,我们常不需要细分到单个灰度值。例如,将 0-255 划分为 16 个区间(每个区间包含 16 个灰度值:0-15、16-31、…、240-255),此时histSize=16,只需 16 个 BINS 就能概览图像的灰度分布,既简化计算,又能突出整体特征。


二、直方图的核心作用

  1. 图像明暗分析:通过直方图峰值位置判断图像偏暗(峰值靠左)、偏亮(峰值靠右)或正常(峰值居中)。
  2. 图像增强依据:针对对比度低的图像(直方图集中在狭窄区间),可通过直方图均衡化扩展灰度范围,提升对比度。
  3. 场景变换检测:在视频处理中,通过对比相邻帧直方图的差异,可检测场景是否切换(如从室内到室外,直方图会显著变化)。
  4. 目标特征提取:在目标检测(如车牌识别、人脸识别)中,直方图可作为纹理特征的一部分,辅助区分目标与背景。

三、OpenCV 计算直方图:calcHist 函数详解

OpenCV 提供cv2.calcHist()函数计算图像直方图,掌握它的参数与用法是实战的关键。

1. 函数语法与参数解析

cv2.calcHist(images, channels, mask, histSize, ranges)

各参数含义如下:

参数说明
images输入图像,格式需为uint8float32,必须用中括号[]包裹(如[img]
channels通道索引,灰度图传[0];彩色图(BGR 格式)传[0](B)、[1](G)、[2](R)
mask掩模图像,统计整幅图像直方图传None;统计局部区域传自定义掩模
histSizeBINS 数量,需用中括号包裹(如[256]表示 256 个区间,[16]表示 16 个区间)
ranges像素值范围,通常为[0, 256](包含 0,不包含 256,覆盖所有灰度值)

2. 基础实战:计算灰度图直方图

以一张手机图像(phone.png)为例,先将其转为灰度图,再计算并绘制直方图。

代码实现

import cv2
import matplotlib.pyplot as plt
import numpy as np# 1. 读取灰度图
phone_gray = cv2.imread('phone.png', cv2.IMREAD_GRAYSCALE)
# 2. 方法1:用matplotlib直接绘制(需先将图像转为一维数组)
# ravel()函数:将二维灰度图转为一维像素数组
pixel_array = phone_gray.ravel()
# 绘制直方图(bins=256,即每个灰度值一个区间)
plt.figure(figsize=(10, 4))
plt.hist(pixel_array, bins=256, color='gray', alpha=0.7)
plt.title('Gray Image Histogram (bins=256)')
plt.xlabel('Gray Level (0-255)')
plt.ylabel('Pixel Count')
plt.show()# 3. 方法2:用OpenCV的calcHist计算(bins=16,简化展示)
phone_hist = cv2.calcHist(images=[phone_gray], channels=[0], mask=None, histSize=[16], ranges=[0, 256])
# 绘制calcHist结果(曲线图)
plt.figure(figsize=(10, 4))
plt.plot(phone_hist, color='black', linewidth=2)
plt.title('Gray Image Histogram (bins=16)')
plt.xlabel('BINS (16 intervals)')
plt.ylabel('Pixel Count')
plt.xticks(range(16), [f'{i*16}-{i*16+15}' for i in range(16)], rotation=45)
plt.show()

结果分析

  • bins=256时,直方图能清晰看到每个灰度值的像素分布,适合精细分析;
  • bins=16时,直方图更简洁,可快速判断图像整体明暗(如峰值集中在哪个区间)。

3. 进阶实战:计算彩色图直方图

彩色图像(BGR 格式)需分别计算 B、G、R 三个通道的直方图,通过对比各通道分布,可分析图像的色彩偏向(如红色通道峰值高,说明图像偏红)。

代码实现

# 读取彩色图像(OpenCV默认BGR格式)
phone_color = cv2.imread('phone.png')
# 定义三个通道的颜色(B、G、R)
colors = ('blue', 'green', 'red')
# 分别计算并绘制三个通道的直方图
plt.figure(figsize=(10, 4))
for i, color in enumerate(colors):# 计算当前通道的直方图hist = cv2.calcHist(images=[phone_color], channels=[i], mask=None, histSize=[256], ranges=[0, 256])# 绘制曲线plt.plot(hist, color=color, label=f'{color.upper()} Channel')plt.title('Color Image Histogram (BGR)')
plt.xlabel('Gray Level (0-255)')
plt.ylabel('Pixel Count')
plt.legend()
plt.show()

结果分析

  • 若蓝色通道(blue)的直方图峰值较高,说明图像中蓝色区域较多(如天空、蓝色物体);
  • 若红色通道(red)峰值靠左,说明红色区域偏暗(如暗红色的物体)。

4. 高级实战:用掩模(mask)计算局部直方图

掩模(mask)是一张与原图像尺寸相同的二值图像(像素值为 0 或 255),它能 “筛选” 出原图像中需要分析的区域(掩模中 255 的区域会被统计,0 的区域会被忽略)。

代码实现

# 1. 读取灰度图并显示
phone_gray = cv2.imread('phone.png', cv2.IMREAD_GRAYSCALE)
cv2.imshow('Original Gray Image', phone_gray)
cv2.waitKey(0)# 2. 创建掩模:只保留图像中间区域(50:350行,100:470列)
# 初始化掩模为全黑(0)
mask = np.zeros(phone_gray.shape[:2], dtype=np.uint8)
# 将中间区域设为白色(255)
mask[50:350, 100:470] = 255
cv2.imshow('Mask (White = Target Area)', mask)
cv2.waitKey(0)# 3. 用bitwise_and获取掩模筛选后的图像(可选,直观查看筛选效果)
masked_image = cv2.bitwise_and(phone_gray, phone_gray, mask=mask)
cv2.imshow('Masked Image (Only Target Area)', masked_image)
cv2.waitKey(0)# 4. 计算局部直方图(仅统计掩模中255的区域)
masked_hist = cv2.calcHist(images=[phone_gray], channels=[0], mask=mask, histSize=[256], ranges=[0, 256])
# 绘制局部直方图
plt.figure(figsize=(10, 4))
plt.plot(masked_hist, color='black', linewidth=2)
plt.title('Local Histogram (Masked Area)')
plt.xlabel('Gray Level (0-255)')
plt.ylabel('Pixel Count')
plt.show()# 关闭所有窗口
cv2.destroyAllWindows()

关键说明

  • cv2.bitwise_and():通过掩模与原图像进行 “与运算”,仅保留掩模中 255 的区域,方便直观查看筛选效果;
  • 局部直方图的应用场景:如分析人脸图像中 “眼睛区域” 的灰度分布,可先用人脸检测算法生成眼睛的掩模,再计算局部直方图。

四、直方图均衡化:提升图像对比度

直方图均衡化是基于直方图的图像增强技术,它通过 “拉伸” 图像的灰度分布范围,将集中在狭窄区间的像素分散到 0-255 全范围,从而提升图像对比度。

例如,一张雾天图像(直方图集中在中间灰度区间),经过均衡化后,暗部更暗、亮部更亮,细节更清晰。

1. OpenCV 实现直方图均衡化

OpenCV 提供cv2.equalizeHist()函数,仅支持灰度图(彩色图需先转为 YUV 格式,对亮度通道 Y 进行均衡化)。

代码实现

import cv2
import matplotlib.pyplot as plt# 1. 读取灰度图(假设图像对比度较低)
low_contrast_img = cv2.imread('low_contrast_phone.png', cv2.IMREAD_GRAYSCALE)
# 2. 计算均衡化前的直方图
hist_before = cv2.calcHist([low_contrast_img], [0], None, [256], [0, 256])# 3. 执行直方图均衡化
equalized_img = cv2.equalizeHist(low_contrast_img)
# 4. 计算均衡化后的直方图
hist_after = cv2.calcHist([equalized_img], [0], None, [256], [0, 256])# 5. 对比显示结果
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
# 原图
axes[0, 0].imshow(low_contrast_img, cmap='gray')
axes[0, 0].set_title('Original Image (Low Contrast)')
axes[0, 0].axis('off')
# 原图直方图
axes[0, 1].plot(hist_before, color='black')
axes[0, 1].set_title('Histogram Before Equalization')
axes[0, 1].set_xlabel('Gray Level')
axes[0, 1].set_ylabel('Pixel Count')
# 均衡化后图像
axes[1, 0].imshow(equalized_img, cmap='gray')
axes[1, 0].set_title('Equalized Image (High Contrast)')
axes[1, 0].axis('off')
# 均衡化后直方图
axes[1, 1].plot(hist_after, color='black')
axes[1, 1].set_title('Histogram After Equalization')
axes[1, 1].set_xlabel('Gray Level')
axes[1, 1].set_ylabel('Pixel Count')plt.tight_layout()
plt.show()

结果分析

  • 均衡化前:直方图集中在中间灰度区间,图像整体发灰、对比度低;
  • 均衡化后:直方图均匀分布在 0-255 全范围,图像暗部与亮部分明,细节更清晰。

五、常见问题与解决方案

  1. Q:计算彩色图直方图时,颜色与预期不符?
    A:OpenCV 读取彩色图默认是 BGR 格式,而 matplotlib 显示时默认是 RGB 格式。若需用 matplotlib 显示原图,需先通过cv2.cvtColor(img, cv2.COLOR_BGR2RGB)转换格式,但计算直方图时无需转换(直接用 BGR 通道即可)。

  2. Q:掩模计算局部直方图时,结果全为 0?
    A:检查掩模的尺寸是否与原图像一致(如原图像是(480, 640),掩模也需是(480, 640)),且掩模的像素值是否为0255(不能是其他值)。

  3. Q:均衡化后图像出现 “过曝” 或 “细节丢失”?
    A:普通直方图均衡化会全局拉伸灰度,可能导致局部细节丢失。可改用自适应直方图均衡化cv2.createCLAHE()),它将图像分块处理,保留局部细节,适合明暗差异大的图像。

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

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

相关文章

docke笔记下篇

本地镜像发布到阿里云 本地镜像发布到阿里云流程 镜像的生成方法 基于当前容器创建一个新的镜像,新功能增强 docker commit [OPTIONS] 容器ID [REPOSITORY[:TAG]] OPTIONS说明: OPTIONS说明: -a :提交的镜像作者; -m :提交时的说…

《大数据之路1》笔记2:数据模型

一 数据建模综述 1.1 为什么要数据建模背景: 随着DT时代的来临,数据爆发式增长,如何对数据有序,有结构地分类组织额存储是关键定义: 数据模型时数据组织和存储的方法,强调从业务、数据存取、使用角度 合理存…

“量子能量泵”:一种基于并联电池与电容阵的动态直接升压架构

“量子能量泵”:一种基于并联电池与电容阵的动态直接升压架构摘要:本文揭示了一种革命性的高效电源解决方案,旨在彻底解决低电压、大功率应用中的升压效率瓶颈与电池一致性难题。该方案摒弃传统磁性升压拓扑,创新性地采用并联电池…

DeepSeek实战--自定义工具

1. 背景 当前已经有很多AI基础平台(比如:扣子、Dify),用户可以快速搭建Agent,那怎样将已有的接口能力给大模型调用呢 ? 今天我们来探索一个,非常高效、快捷的方案:将http接口做成Dif…

“移动零”思路与题解

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。思路讲解:举例如下:实现代码是:class Solution { public:v…

关于行内元素,行内块元素和块级元素

1、什么是行内元素,什么是行内块元素,什么是块级元素行内元素的特点:不独占一行,相邻元素会在同一行显示,直到一行排不下才换行。宽度和高度由内容本身决定,无法通过width,height手动设置&#…

⽹络请求Axios的概念和作用

Axios 是一个基于 ​​Promise​​ 的轻量级、高性能 ​​HTTP 客户端库​​,主要用于在浏览器和 Node.js 环境中发起 HTTP 请求(如 GET、POST、PUT、DELETE 等)。它通过简洁的 API 和强大的功能,简化了前端与后端之间的数据交互过…

在AgentScope中实现结构化输出

在AgentScope中实现结构化输出 概述 在AgentScope框架中,结构化输出功能允许开发者定义明确的输出模式,确保AI模型的响应符合预期的格式和约束。本教程将介绍如何使用AgentScope的structured_model参数来实现结构化输出。 结构化输出的优势 数据一致性&a…

Linux 磁盘I/O高占用进程排查指南:从定位到分析的完整流程

在Linux服务器运维工作中,磁盘I/O瓶颈是导致系统性能下降的常见原因之一。当服务器出现响应缓慢、应用卡顿等问题时,及时定位并解决高I/O占用进程就显得尤为重要。本文将从核心思路出发,通过“确认问题-定位磁盘-锁定进程-深入分析”四个步骤…

解决React中通过外部引入的css/scss/less文件更改antDesign中Modal组件内部的样式不生效问题

不生效原因Ant Design 的 Modal 默认通过 ReactDOM.createPortal 挂在 <body> 下&#xff0c;与你的组件树平级&#xff0c;所以写在 .module.css / scoped less 里的选择器根本匹配不到它&#xff0c;就算写全局样式&#xff0c;也可能因为权重不足或异步挂载时机而“看…

day41 51单片机最小系统、GPIO控制、时序逻辑器件(74HC138/595)与LED点阵驱动原理

day41 51单片机最小系统、GPIO控制、时序逻辑器件&#xff08;74HC138/595&#xff09;与LED点阵驱动原理一、嵌入式系统基础概念 1.1 嵌入式系统定义先设计硬件&#xff0c;基于硬件设计软件实现一个具体的功能 —— 专用的计算机系统硬件/软件可剪裁&#xff1a;根据功能需求…

html列表总结补充

1.有序列表的type属性不同的type值表示不同的排序标号1 表示列表项目用数字标号&#xff08;1,2,3...&#xff09; 1 a 表示列表项目用小写字母标号&#xff08;a,b,c...&#xff09; 2 A 表示列表项目用大写字母标号&#xff08;A,B,C...&#xff09; 3 i 表示列表项目用小写罗…

smartctl Current_Pending_Sector 硬盘待处理扇区

smartctl -a /dev/sdae当前值: 312 个待处理扇区 严重警告信号&#xff0c;硬盘发现了 312 个可疑扇区&#xff0c;正在等待重新分配 197 Current_Pending_Sector 0x0022 100 100 000 Old_age Always - 312读取错误频发 错误计数: 38 次 ATA 错误 …

MATLAB1-基本操作和矩阵输入-台大郭彦甫

目录 基础的指令 format 矩阵和向量 找出某行某列的矩阵元素 快速打出多个矩阵或者向量 矩阵连接 矩阵计算 一些特殊矩阵fuction 矩阵相关函数 基础的指令 clc 清空命令行窗口 clear all 清空工作区的全部变量 who 将工作区的全部变量显示出来 whos 工作区的变量信息详…

【CSS 3D 交互】实现精美翻牌效果:从原理到实战

效果图 前言 在现代网页设计中&#xff0c;交互效果是提升用户体验的重要手段。3D 翻牌效果作为一种常见的交互模式&#xff0c;广泛应用于卡片展示、问答切换、产品详情等场景。本文将详细介绍如何使用 CSS 3D 技术实现一个精美的翻牌效果&#xff0c;并深入解析其实现原理。…

Python核心技术开发指南(062)——静态方法

版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 静态方法的定义 静态方法是类中定义的一种特殊方法,它不需要依赖类实例或类本身即可调用,也不隐含传递self(实例引用)或cls(类引用)参数。在Python中,通过@staticmethod装饰器来定义静态…

炒股进阶理论知识

学完前面的《从零开始学炒股》这样的入门课程后&#xff0c;你已經有了一个基本的框架&#xff0c;接下来需要做的是深化、拓展和建立自己的交易系统。以下是为你量身定制的后续学习路径和理论知识建议&#xff0c;分为几个核心模块&#xff1a;模块一&#xff1a;技术分析的深…

华为OD机试真题-跳马-OD统一考试(C卷)

题目描述: 马是象棋(包括中国象棋和国际象棋)中的棋子,走法是每步直一格再斜一格,即先横着或直着走一格,然后再斜着走一个对角线,可进可退,可越过河界,俗称“马走‘日’字。 给顶m行n列的棋盘(网格图),棋盘上只有有棋子象棋中的棋子“马”,并且每个棋子有等级之分,…

PyTorch 模型保存与加载 (速查版)

文章目录1. 推理用: 保存 & 加载权重 (最常见)2. 继续训练用: 保存 & 加载完整状态3. 微调用: 部分加载 (分类头不同等情况)1. 推理用: 保存 & 加载权重 (最常见) import torch import torch.nn as nnmodel nn.Linear(10, 2)# 保存权重 torch.save(model.state_d…

oneshape acad数据集 sam-dataset

Full Text Search - Hugging Face sketchai (Sketch AI)