OpenCV 直方图概念,直方图均衡化原理详解

news2024/11/25 4:18:13

文章目录

  • 直方图相关概念
    • 颜色灰度级
    • 作用
    • 应用场景
  • C++ 使用OpenCV绘制直方图
    • 单通道直方图
      • 关键代码分析:
        • `calcHist`函数分析
        • 使用OpenCV API来绘制直方图
      • 效果图:
    • 彩色三通道直方图
      • 效果图:
  • 直方图均衡化概念
    • 均衡化作用
    • 均衡化效果
    • 均衡化数学原理
      • 步骤
      • 数学公式
      • 示例
    • OpenCV 直方图均衡化 API

直方图相关概念

直方图(Histogram)是一种图表,用于显示数据集的分布情况。它通过将数据范围划分为若干个连续的区间(称为“桶”或“箱”—bins),然后统计每个区间内的数据点数量,以条形图的形式展示这些数量。横轴表示数据的取值范围,纵轴表示每个区间内数据点的数量或频率。例如下图(转载自025 - 二维直方图_哔哩哔哩_bilibili)

在这里插入图片描述

颜色灰度级

  • 灰度级:在灰度图像中,每个像素的亮度值,可以是0到255之间的整数,其中0表示黑色,255表示白色,中间的值表示不同程度的灰色。
  • 直方图:一种柱状图,用于表示每个灰度级在图像中出现的频率。

作用

  1. 图像分析:直方图可以帮助分析图像的整体亮度分布,了解图像是过暗、过亮还是对比度适中。
  2. 图像增强:通过直方图均衡化等技术,可以增强图像的对比度,使图像细节更加清晰。
  3. 图像分割:在图像处理和计算机视觉中,可以利用直方图来确定图像的阈值,进行目标和背景的分割。
  4. 图像对比度调整:根据直方图调整图像对比度,使图像更符合视觉需求。

应用场景

  1. 医学影像处理:在医学图像(如X射线、CT、MRI)中,通过直方图分析可以增强图像,帮助医生更好地诊断。
  2. 摄影和图像编辑:摄影师和图像编辑软件利用直方图调整图像的曝光、对比度和颜色分布,以达到理想的视觉效果。
  3. 监控系统:在监控图像中,直方图可以帮助调整图像质量,使得图像在不同光照条件下保持较好的可见性。
  4. 计算机视觉:在目标检测、识别和跟踪中,直方图作为特征描述子之一,可以用于比较和匹配图像。

C++ 使用OpenCV绘制直方图

单通道直方图

顾名思义,就是分析单通道图像的亮度分布情况,一般用于灰度图像,下方代码,则是展示了分析三通道图像的其中一个通道的直方图

void show_hist_demo(Mat &image){
    Mat hist;
    vector<Mat> bgr_channels;
    split(image,bgr_channels);
    int histSize = 256; // 从 0 到 255
    float range[] = { 0, 256 }; // 强度范围 灰度等级
    const float* histRange = { range };  // calcHist支持分析多张图片,这里只提供一张图片,因此,只传入一个范围数组。分析多张图片,往后添加范围数组即可
    calcHist(&bgr_channels[0], 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false);
    // 绘制直方图
    int hist_w = 512, hist_h = 400;
    int bin_w = cvRound((double) hist_w / histSize);
    Mat histImage(hist_h, hist_w, CV_8UC1, Scalar(0, 0, 0));  // 空白图像 用来显示结果
    normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());

    for (int i = 1; i < histSize; i++) {
        line(histImage,
             Point(bin_w * (i - 1), hist_h - cvRound(hist.at<float>(i - 1))),
             Point(bin_w * i, hist_h - cvRound(hist.at<float>(i))),
             Scalar(255), 2, 8, 0);
    }

    imshow("Source image", image);
    imshow("Histogram", histImage);
    waitKey(0);
}

关键代码分析:

vector<Mat> bgr_channels;
split(image,bgr_channels);

将一张彩色三通道图像分离成三个通道 B G R Mat对象。便于下文分析单个通道的分析。

calcHist函数分析

函数原型

void cv::calcHist(const Mat* images, 
                  int nimages, 
                  const int* channels, 
                  InputArray mask, 
                  OutputArray hist, 
                  int dims, 
                  const int* histSize, 
                  const float** ranges, 
                  bool uniform = true, 
                  bool accumulate = false);

参数介绍

const Mat* images:

  • 这是指向图像数组的指针,可以处理单张或多张图像。在单通道或者多通道图像的处理中,这个数组将包含所有需要计算直方图的图像。

int nimages:

  • 这个参数指定了 images 数组中图像的数量。例如,如果你只处理一张图像,这个值就应该是 1。

const int* channels:

  • 这是一个包含了需要计算直方图的通道索引的数组。例如,对于 BGR 图像,如果你只想计算蓝色通道的直方图,你可以传递 0。

InputArray mask:

  • 这是一个可选参数,用于定义一个掩码。如果掩码非空,函数将只计算掩码内像素的直方图。这可以用于计算图像特定区域的直方图。

OutputArray hist:

  • 这是一个输出参数,用来存储计算得到的直方图。通常,这是一个多维数组,其中每个维度对应于一个通道或一个特定的直方图轴。

int dims:

  • 这指定了直方图的维度数量。对于简单的灰度图像直方图,这个值是 1。对于包含多个通道的图像,这个值可以更高。

const int* histSize:

  • 这是一个数组,指定了每个维度的 bin 数量。例如,如果你在每个颜色通道上想有 256 个 bins,你可以传递 {256, 256, 256}

const float\** ranges:

  • 这是一个指向数组的指针,数组中每个元素定义了直方图的值范围。对于标准的 8 位图像,这通常是 {0, 256}

bool uniform:

  • 这个布尔值指定直方图是否均匀。如果为 true,表示所有的 bin 的宽度都是相同的。这通常可以提高计算的效率。

bool accumulate:

  • 如果这个值被设为 true,则直方图在调用时不会被清空。这允许从多个数组中累积直方图,或者用于时间上连续的直方图更新。

举个栗子

 calcHist(&bgr_channels[0], 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false);

这行代码则是 只计算通道B(蓝色)像素值分布,只计算一张图片,第一个通道,不需要使用掩码,输出到hist,数量为1维,范围为0-255的灰度等级,使所有的bin的宽度相同,调用完毕之后清空直方图,不进行累计。

使用OpenCV API来绘制直方图

上述代码已经得到了结果,存放在hist 中, 接下来需要创建一个空的图像,将结果绘制上去。

1.首先进行归一化,目的确保 条目的数量不超过空白图像的高度 ,因此将条目的数量 归一到0到最高点的范围之内。

 normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());

2.遍历 0-255 每个条目数量,使用 OpenCV line API 绘制到图像中。使用 line 函数绘制直方图时,通过 Point 类创建线的起点和终点。这段代码是用来将计算出的直方图的数据可视化为一个图像。每一次循环中的 line 函数调用都会在直方图图像上绘制一条小线段,这些线段合起来形成了完整的直方图。

bin_w * (i - 1):这计算了当前 bin 的起始水平位置

hist_h - cvRound(hist.at<float>(i - 1)):这计算了线段起点的垂直位置。hist_h 是直方图图像的总高度,hist.at<float>(i - 1) 从直方图数据中获取当前 bin (i-1) 的值(这个值表示该强度值的像素数量),使用 cvRound 将其四舍五入到最近的整数。从 hist_h 中减去这个值将坐标翻转,因为在图像坐标中,y 轴是向下增长的。

  for (int i = 1; i < histSize; i++) {
        line(histImage,
             Point(bin_w * (i - 1), hist_h - cvRound(hist.at<float>(i - 1))),
             Point(bin_w * i, hist_h - cvRound(hist.at<float>(i))),
             Scalar(255), 2, 8, 0);
    }

效果图:

在这里插入图片描述

彩色三通道直方图

可以直接调用calcHist函数输出三个通道的直方图,也可以分三次调用单通道直方图代码,本文采用了后者

void show_dims_hist_demo(Mat &image){
    // 计算直方图
    vector<Mat> bgr_channels;
    split(image,bgr_channels);
    int histSize = 256; // 从 0 到 255
    float range[] = { 0, 256 }; // 强度范围 灰度等级
    const float* histRange = { range };
    int hist_w = 512, hist_h = 400;
    int bin_w = cvRound((double) hist_w / histSize);
    Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));  // 使用彩色图像以显示三个通道
    vector<Scalar> colors = {Scalar(255, 0, 0), Scalar(0, 255, 0), Scalar(0, 0, 255)};  // 蓝、绿、红
    for (int c = 0; c < 3; c++) {
        Mat hist;
        calcHist(&bgr_channels[c], 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false);
        normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
        // 绘制直方图
        for (int i = 1; i < histSize; i++) {
            line(histImage,
                 Point(bin_w * (i - 1), hist_h - cvRound(hist.at<float>(i - 1))),
                 Point(bin_w * i, hist_h - cvRound(hist.at<float>(i))),
                 colors[c], 2, 8, 0);
        }
    }
    imshow("Histogram", histImage);
    waitKey(0);
}

与单通道唯一不同的是,调用了for循环,绘制了三条曲线代表 B G R 三个通道

效果图:

在这里插入图片描述

直方图均衡化概念

直方图均衡化(Histogram Equalization)是一种图像处理技术,用于增强图像的对比度。其目的是通过调整灰度级分布,使得图像的灰度级在直方图上的分布更加均匀,从而提升图像的视觉效果

均衡化作用

  1. 增强图像对比度:通过均衡化,原本对比度较低的图像可以变得更加清晰,细节更加明显。
  2. 改善图像质量:直方图均衡化可以使图像在视觉上更加均衡和舒适,特别是在光照条件不佳的情况下。
  3. 提高图像处理算法的效果:许多图像处理和计算机视觉算法在对比度较高的图像上表现更好,直方图均衡化可以作为预处理步骤提高这些算法的性能。

均衡化效果

左边为原始图像,右边为均衡化之后的图像

在这里插入图片描述

均衡化数学原理

直方图均衡化的数学原理涉及图像的累积分布函数(CDF)和灰度级映射。以下是其具体步骤和公式:

步骤

  1. 计算图像的直方图
    设原始图像有 (N) 个像素,每个像素的灰度级范围为 (0) 到 (L-1),计算每个灰度级 (i) 的像素数量 (n_i),并得到图像的概率密度函数(PDF):
    p ( i ) = n i N , i = 0 , 1 , 2 , … , L − 1 p(i) = \frac{n_i}{N}, \quad i = 0, 1, 2, \ldots, L-1 p(i)=Nni,i=0,1,2,,L1

  2. 计算累积分布函数(CDF)
    累积分布函数 (c(i)) 是概率密度函数 (p(i)) 的累加和:
    c ( i ) = ∑ j = 0 i p ( j ) , i = 0 , 1 , 2 , … , L − 1 c(i) = \sum_{j=0}^{i} p(j), \quad i = 0, 1, 2, \ldots, L-1 c(i)=j=0ip(j),i=0,1,2,,L1

  3. 映射旧灰度级到新灰度级
    利用累积分布函数将旧的灰度级 (i) 映射到新的灰度级 (i’),映射关系为:
    i ′ = ⌊ ( L − 1 ) ⋅ c ( i ) ⌋ i' = \left\lfloor (L-1) \cdot c(i) \right\rfloor i=(L1)c(i)
    向下取整

  4. 生成均衡化后的图像
    使用上述映射关系,将原始图像中的每个像素灰度级 (i) 映射为新的灰度级 (i’),得到均衡化后的图像。

数学公式

设原始图像的灰度级为 (X),均衡化后的灰度级为 (Y),其关系可以表示为:
Y = T ( X ) Y = T(X) Y=T(X)
其中 T(X) 是映射函数,定义为:
T ( X ) = ( L − 1 ) ⋅ ∫ 0 X p ( x )   d x T(X) = (L-1) \cdot \int_{0}^{X} p(x) \, dx T(X)=(L1)0Xp(x)dx

对于离散情况,映射关系可以写为:

Y i = ( L − 1 ) ⋅ ∑ j = 0 i p ( j ) Y_i = (L-1) \cdot \sum_{j=0}^{i} p(j) Yi=(L1)j=0ip(j)

示例

假设一个简单的8位灰度图像,其灰度级范围为 (0) 到 (255)。原始图像的灰度直方图如下:

灰度级 (i)像素数量 (n_i)概率密度函数 (p(i))累积分布函数 (c(i))新灰度级 (i’)
05000.050.0512
110000.100.1538
215000.150.3076
320000.200.50127
415000.150.65165
510000.100.75191
65000.050.80204
75000.050.85217
85000.050.90229
95000.050.95242
105000.051.00255

根据上述表格,新灰度级的映射关系已经确定,可以用于生成均衡化后的图像。

直方图均衡化通过这种方式重新分配灰度级,使得图像的整体对比度得到增强,从而使图像细节更加清晰。

OpenCV 直方图均衡化 API

OpenCV 提供了用于直方图均衡化的函数 equalizeHist。这个函数可以对单通道(通常是灰度图像)进行直方图均衡化

使用示例

int main()
{
    string imagePath = "C:\\Users\\Marxist\\Pictures\\coco\\raw_lenna.jpg";
    string video_path = "C:\\Users\\Marxist\\Videos\\Captures\\gtr.mp4";
    Mat image = imread(imagePath,IMREAD_GRAYSCALE);
    if(image.empty()){
        cout<<"file not exist!"<<endl;
        return -1;
    }
    Mat dst;
    equalizeHist(image,dst);
    imshow("raw image",image);
    imshow("equalizeHist image",dst);
    waitKey(0);
    return 0;
}

注: equalizeHist 只能用于单通道。可以对彩色图像的每个通道分别进行直方图均衡化,或更常见的是将彩色图像转换到 YCrCb 或 HSV 色彩空间,在亮度通道上应用直方图均衡化,然后再转换回 RGB 色彩空间。

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

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

相关文章

项目实战二 HIS项目

目标&#xff1a; 项目的操作流程&#xff1a; 开发体系 前端开发&#xff1a;负责页面的编写 HTML CSS JavaScript 后端开发&#xff1a;看不到 摸不着的功能 常用开发语言 PHP JAVA Python 框架 &#xff1a; 半成品 做好的功能模块 版本控制 Git 分布式版本控…

vxe-table——实现切换页码时排序状态的回显问题(ant-design+elementUi中table排序不同时回显的bug)——js技能提升

之前写的后台管理系统&#xff0c;都是用的antdelement&#xff0c;table组件中的【排序】问题是有一定的缺陷的。 想要实现的效果&#xff1a; antv——table组件一次只支持一个参数的排序 如下图&#xff1a; 就算是可以自行将排序字段拼接到列表接口的入参中&#xff0c…

【中项】系统集成项目管理工程师-第4章 信息系统架构-4.3应用架构

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

vue3 -layui项目-左侧导航菜单栏

1.创建目录结构 进入cmd,先cd到项目目录&#xff08;项目vue3-project&#xff09; cd vue3-project mkdir -p src\\views\\home\\components\\menubar 2.创建组件文件 3.编辑menu-item-content.vue <template><template v-if"item.icon"><lay-ic…

计算机网络八股文(后续更新)

文章目录 一、计算机网络体系结构1、计算机网络的各层协议及作用&#xff1f; 二、物理层三、数据链路层四、网络层五、传输层1、TCP和UDP的区别&#xff1f;2、UDP 和 TCP 对应的应用场景是什么&#xff1f;3、详细介绍一下 TCP 的三次握手机制4、为什么需要三次握手&#xff…

Electron 结合 Selenium + chromedriver 驱动服务实现浏览器多开

背景 在调研浏览器多开的过程中&#xff0c;electron 有自带的 browserview&#xff0c;webview&#xff0c;但是上面两个受制于 electron 内核版本限制&#xff0c;升级不够灵活&#xff0c;对新版的网页支持可能不及时&#xff0c;甚至不兼容&#xff0c;必须通过发布新的客…

MSP430M03507最小系统板的keil环境搭配,用keil编辑ti单片机

转载自嘉立创MSP430M03507开发手册 这篇文章只是因为我的keil版本与嘉立创的不一样&#xff0c;所以添加了我自己遇到的问题解析 先说说为什么要用keil编辑&#xff0c;因为ti单片机自己的ccs编译环境需要对应仿真器&#xff0c;那个加芯片都240了&#xff0c;哪有那么多钱买…

JAVA开发工具IDEA如何连接操作数据库

一、下载驱动 下载地址&#xff1a;【免费】mysql-connector-j-8.2.0.jar资源-CSDN文库 二、导入驱动 鼠标右击下载到IDEA中的jar包&#xff0c;选择Add as Library选项 如图就导入成功 三、加载驱动 Class.forName("com.mysql.cj.jdbc.Driver"); 四、驱动管理…

Bootstrap5 Navbar多级下拉框

实现目标&#xff1a; 1、访问 Bootstrap5-navbar 2、修改dropdown为多级 <!DOCTYPE HTML> <html lang"en-US"> <head><meta charset"UTF-8"><title></title><link rel"stylesheet" href"https…

视频汇聚平台EasyCVR启动出现报错“cannot open shared object file”的原因排查与解决

安防视频监控EasyCVR安防监控视频系统采用先进的网络传输技术&#xff0c;支持高清视频的接入和传输&#xff0c;能够满足大规模、高并发的远程监控需求。EasyCVR平台支持多种视频流的外部分发&#xff0c;如RTMP、RTSP、HTTP-FLV、WebSocket-FLV、HLS、WebRTC、fmp4等&#xf…

python实现图像特征提取算法1

python实现Marr-Hildreth算法、Canny边缘检测器算法 1.Marr-Hildreth算法详解算法步骤公式Python 实现详细解释优缺点 2.Canny边缘检测器算法详解算法步骤公式Python 实现详细解释优缺点 1.Marr-Hildreth算法详解 Marr-Hildreth算法是一个用于图像边缘检测的经典算法&#xff…

一些和颜色相关网站

1.中国传统色 2.网页颜色选择器 3.渐变色网站 4.多风格色卡生成 5.波浪生成 6.半透明磨砂框

基于3D开发引擎HOOPS平台的大型三维PLM系统的设计、开发与应用

产品生命周期管理&#xff08;Product Lifecycle Management&#xff0c;PLM&#xff09;系统在现代制造业中扮演着至关重要的角色。随着工业4.0和智能制造的推进&#xff0c;PLM系统从最初的CAD和PDM系统发展到现在的全面集成、协作和智能化的平台。本文将探讨基于HOOPS平台的…

四大引用——强软弱虚

目录 一、强引用 二、软引用 三、弱引用 四、虚引用 一、强引用 强引用是在程序代码之中普遍存在的&#xff0c;类似于“Object obj new Object()”&#xff0c;obj变量引用Object这个对象&#xff0c;就叫做强引用。当内存空间不足&#xff0c;Java虚拟机宁愿抛出OutOfMe…

乐鑫AWS IoT ExpressLink方案,简化物联网设备连接AWS IoT服务

在现代科技迅速发展的今天&#xff0c;物联网&#xff08;IoT&#xff09;已经成为连接物理世界与数字世界的重要桥梁&#xff0c;越来越多的设备开始接入网络&#xff0c;实现智能化控制。 在这个大背景下&#xff0c;乐鑫携手亚马逊&#xff0c;推出了AWS IoT ExpressLink方…

【机器学习】机器学习解决的问题特点、机器学习学的是什么、怎么学、如何构建高效机器学习模型的策略、机器学习的分类以及机器学习、模式识别、数据挖掘和人工智能的区别

引言 机器学习是人工智能的一个重要分支&#xff0c;主要解决的是如何通过算法让机器从数据中自动学习规律和知识&#xff0c;以完成特定任务或解决特定问题。 文章目录 引言一、机器学习解决的是什么样的问题1.2 数据驱动的预测问题1.3 数据理解与挖掘1.4 优化与决策问题1.5 异…

一个简单好用安全的开源交互审计系统,支持SSH,Telnet,Kubernetes协议(带私活)

前言 在当今的企业网络环境中&#xff0c;远程访问和交互审计成为了保障网络安-全的重要组成部分。然而&#xff0c;现有的解-决方案往往存在一些痛点&#xff0c;如复杂的配置、有限的协议支持、以及审计功能的不足。这些问题不仅增加了IT管理员的负担&#xff0c;也为企业的…

泰迪智能科技携广州华商学院共讨产教融合,校企合作

7月19日&#xff0c;广州华商学院人工智能学院的领导及骨干教师一行莅临泰迪智能科技参观交流&#xff0c;广州华商学院人工智能学院院长助理杨本胜、院长助理洪绍勇、大数据系主任颜远海、金融数学系主任石金诚、人工智能系主任霍永良&#xff0c;以及骨干教师许丽娟、李志青、…

二十、【机器学习】【非监督学习】- 均值漂移 (Mean Shift)

系列文章目录 第一章 【机器学习】初识机器学习 第二章 【机器学习】【监督学习】- 逻辑回归算法 (Logistic Regression) 第三章 【机器学习】【监督学习】- 支持向量机 (SVM) 第四章【机器学习】【监督学习】- K-近邻算法 (K-NN) 第五章【机器学习】【监督学习】- 决策树…

Electron 渲染进程直接调用主进程的API库@electron/remote引用讲解

背景 remote是个老库&#xff0c;早期Electron版本中有个remote对象&#xff0c;这个对象可以横跨所有进程&#xff0c;随意通信&#xff0c;后来官方认为不安全&#xff0c;被干掉了&#xff0c;之后有人利用Electron的IPC通信&#xff0c;底层通过Promise的await能力&#x…