图像处理基础(8):图像的灰度直方图、直方图均衡化、直方图规定化(匹配)

本文主要介绍了灰度直方图相关的处理,包括以下几个方面的内容:

•   利用OpenCV计算图像的灰度直方图,并绘制直方图曲线

•   直方图均衡化的原理及实现

•   直方图规定化(匹配)的原理及实现

图像的灰度直方图

一幅图像由不同灰度值的像素组成,图像中灰度的分布情况是该图像的一个重要特征。图像的灰度直方图就描述了图像中灰度分布情况,能够很直观的展示出图像中各个灰度级所占的多少。
图像的灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数:其中,横坐标是灰度级,纵坐标是该灰度级出现的频率。

不过通常会将纵坐标归一化到

区间内,也就是将灰度级出现的频率(像素个数)除以图像中像素的总数。灰度直方图的计算公式如下:

其中,

是像素的灰度级,

是具有灰度

的像素的个数,

是图像中总的像素个数。

OpenCV灰度直方图的计算

直方图的计算是很简单的,无非是遍历图像的像素,统计每个灰度级的个数。在OpenCV中封装了直方图的计算函数calcHist,为了更为通用该函数的参数有些复杂,其声明如下:

void 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 );

该函数能够同时计算多个图像,多个通道,不同灰度范围的灰度直方图.
其参数如下:

•   images,输入图像的数组,这些图像要有相同大大小,相同的深度(CV_8U CV_16U CV_32F).

•   nimages ,输入图像的个数

•   channels,要计算直方图的通道个数。

•   mask,可选的掩码,不使用时可设为空。要和输入图像具有相同的大小,在进行直方图计算的时候,只会统计该掩码不为0的对应像素

•   hist,输出的直方图

•   dims,直方图的维度

•   histSize,直方图每个维度的大小

•   ranges,直方图每个维度要统计的灰度级的范围

•   uniform,是否进行归一化,默认为true

•   accumulate,累积标志,默认值为false。

为了计算的灵活性和通用性,OpenCV的灰度直方图提供了较多的参数,但对于只是简单的计算一幅灰度图的直方图的话,又显得较为累赘。这里对calcHist进行一次封装,能够方便的得到一幅灰度图直方图。

class Histogram1D
{
private:
    int histSize[1]; // 项的数量
    float hranges[2]; // 统计像素的最大值和最小值
    const float* ranges[1];
    int channels[1]; // 仅计算一个通道

public:
    Histogram1D()
    {
        // 准备1D直方图的参数
        histSize[0] = 256;
        hranges[0] = 0.0f;
        hranges[1] = 255.0f;
        ranges[0] = hranges;
        channels[0] = 0;
    }

    MatND getHistogram(const Mat &image)
    {
        MatND hist;
        // 计算直方图
        calcHist(&image ,// 要计算图像的
            1,                // 只计算一幅图像的直方图
            channels,        // 通道数量
            Mat(),            // 不使用掩码
            hist,            // 存放直方图
            1,                // 1D直方图
            histSize,        // 统计的灰度的个数
            ranges);        // 灰度值的范围
        return hist;
    }

    Mat getHistogramImage(const Mat &image)
    {
        MatND hist = getHistogram(image);

        // 最大值,最小值
        double maxVal = 0.0f;
        double minVal = 0.0f;

        minMaxLoc(hist, &minVal, &maxVal);

        //显示直方图的图像
        Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));

        // 设置最高点为nbins的90%
        int hpt = static_cast<int>(0.9 * histSize[0]);
        //每个条目绘制一条垂直线
        for (int h = 0; h < histSize[0]; h++)
        {
            float binVal = hist.at<float>(h);
            int intensity = static_cast<int>(binVal * hpt / maxVal);
            // 两点之间绘制一条直线
            line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));
        }
        return histImg;
    }
};

Histogram1D提供了两个方法:getHistogram返回统计直方图的数组,默认计算的灰度范围是[0,255];getHistogramImage将图像的直方图以线条的形式画出来,并返回包含直方图的图像。测试代码如下:

    Histogram1D hist;
    Mat histImg;
    histImg = hist.getHistogramImage(image);

    imshow("Image", image);
    imshow("Histogram", histImg);

其结果如下:

直方图均衡化 Histogram Equalization

假如图像的灰度分布不均匀,其灰度分布集中在较窄的范围内,使图像的细节不够清晰,对比度较低。通常采用直方图均衡化直方图规定化两种变换,使图像的灰度范围拉开或使灰度均匀分布,从而增大反差,使图像细节清晰,以达到增强的目的。
直方图均衡化,对图像进行非线性拉伸,重新分配图像的灰度值,使一定范围内图像的灰度值大致相等。这样,原来直方图中间的峰值部分对比度得到增强,而两侧的谷底部分对比度降低,输出图像的直方图是一个较为平坦的直方图。

均衡化算法

直方图的均衡化实际也是一种灰度的变换过程,将当前的灰度分布通过一个变换函数,变换为范围更宽、灰度分布更均匀的图像。也就是将原图像的直方图修改为在整个灰度区间内大致均匀分布,因此扩大了图像的动态范围,增强图像的对比度。通常均衡化选择的变换函数是灰度的累积概率,直方图均衡化算法的步骤:

•   计算原图像的灰度直方图 

,其中

为像素总数,

为灰度级

的像素个数

•   计算原始图像的累积直方图 

•   

,其中 

是目的图像的像素,

是源图像灰度为i的累积分布,L是图像中最大灰度级(灰度图为255)

其代码实现如下:

•   在上面中封装了求灰度直方图的类,这里直接应用该方法得到图像的灰度直方图;

•   将灰度直方图进行归一化,计算灰度的累积概率;

•   创建灰度变化的查找表

•   应用查找表,将原图像变换为灰度均衡的图像

具体代码如下:

void equalization_self(const Mat &src, Mat &dst)
{
    Histogram1D hist1D;
    MatND hist = hist1D.getHistogram(src);

    hist /= (src.rows * src.cols); // 对得到的灰度直方图进行归一化
    float cdf[256] = { 0 }; // 灰度的累积概率
    Mat lut(1, 256, CV_8U); // 灰度变换的查找表
    for (int i = 0; i < 256; i++)
    {
        // 计算灰度级的累积概率
        if (i == 0)
            cdf[i] = hist.at<float>(i);
        else
            cdf[i] = cdf[i - 1] + hist.at<float>(i);

        lut.at(i) = static_cast(255 * cdf[i]); // 创建灰度的查找表
    }

    LUT(src, lut, dst); // 应用查找表,进行灰度变化,得到均衡化后的图像

}

上面代码只是加深下对均衡化算法流程的理解,实际在OpenCV中也提供了灰度均衡化的函数equalizeHist,该函数的使用很简单,只有两个参数:输入图像,输出图像。下图为,上述代码计算得到的均衡化结果和调用equalizeHist的结果对比

最左边为原图像,中间为OpenCV封装函数的结果,右边为上面代码得到的结果。

直方图规定化

从上面可以看出,直方图的均衡化自动的确定了变换函数,可以很方便的得到变换后的图像,但是在有些应用中这种自动的增强并不是最好的方法。有时候,需要图像具有某一特定的直方图形状(也就是灰度分布),而不是均匀分布的直方图,这时候可以使用直方图规定化
直方图规定化,也叫做直方图匹配,用于将图像变换为某一特定的灰度分布,也就是其目的的灰度直方图是已知的。这其实和均衡化很类似,均衡化后的灰度直方图也是已知的,是一个均匀分布的直方图;而规定化后的直方图可以随意的指定,也就是在执行规定化操作时,首先要知道变换后的灰度直方图,这样才能确定变换函数。规定化操作能够有目的的增强某个灰度区间,相比于,均衡化操作,规定化多了一个输入,但是其变换后的结果也更灵活。

在理解了上述的均衡化过程后,直方图的规定化也较为简单。可以利用均衡化后的直方图作为一个中间过程,然后求取规定化的变换函数。具体步骤如下:

•   将原始图像的灰度直方图进行均衡化,得到一个变换函数

,其中s是均衡化后的像素,r是原始像素

•   对规定的直方图进行均衡化,得到一个变换函数

,其中v是均衡化后的像素,z是规定化的像素

•   上面都是对同一图像的均衡化,其结果应该是相等的,

,且

通过,均衡化作为中间结果,将得到原始像素

规定化后像素之间的映射关系。

详解规定化过程

对图像进行直方图规定化操作,原始图像的直方图和以及规定化后的直方图是已知的。假设

表示原始图像的灰度概率密度,

表示规定化图像的灰度概率密度(r和z分别是原始图像的灰度级,规定化后图像的灰度级)。

•   对原始图像进行均衡化操作,则有

•   对规定化的直方图进行均衡化操作,则

•   由于是对同一图像的均衡化操作,所以有

•   规定化操作的目的就是找到原始图像的像素

到规定化后图像像素的

之间的一个映射。有了上一步的等式后,可以得到

,因此要想找到

相对应的

只需要在

进行迭代,找到使式子

的绝对值最小即可。

•   上述描述只是理论的推导过程,在实际的计算过程中,不需要做两次的均衡化操作,具体的推导过程如下:$$
 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++-第十二章: AVL树

目录 第一节&#xff1a;AVL树的特征 第二节&#xff1a;实现思路 2-1.插入 2-1-1.右单旋 2-1-2.左单旋 2-1-3.左右双旋 2-1-4.右左双旋 2-1-5.总结 2-2.删除 第三节&#xff1a;代码实现 3-1.Node类 3-2.AVLTree类 3-2-1.Insert函数 3-2-2.Height函数 3-2-3.Balance函数 3-…

学习路程八 langchin核心组件 Models补充 I/O和 Redis Cache

前序 之前了解了Models&#xff0c;Prompt&#xff0c;但有些资料又把这块与输出合称为模型输入输出&#xff08;Model I/O&#xff09;‌&#xff1a;这是与各种大语言模型进行交互的基本组件。它允许开发者管理提示&#xff08;prompt&#xff09;&#xff0c;通过通用接口调…

【fnOS飞牛云NAS本地部署DeepSeek-R1结合内网穿透远程访问告别服务器繁忙】

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

ISIS(中间系统到中间系统)——基础

ISIS是一项通用的动态路由协议&#xff0c;其隶属于链路状态路由协议&#xff0c;最初运行与OSI七层的网络层&#xff0c;采用组播地址224.0.0.14和224.0.0.15两个组波段&#xff0c;由于其较高的拓展性与高速收敛&#xff0c;被大多数运营商网络所使用 起源 ISIS最初是由国际…

DeepSeek本地部署:开启智能搜索的本地之旅

前言 嘿&#xff0c;朋友们&#xff01;最近国产大模型DeepSeek特别火&#xff0c;以至于频繁出现反应迟缓甚至宕机的情况&#xff0c;和两年前ChatGPT刚推出时的遭遇颇为相似。这让我想起了那句老话&#xff1a;“自己动手&#xff0c;丰衣足食”。万幸的是&#xff0c;DeepSe…

初会学习记录

【25初级会计《实务》】第一章&#xff1a;权责发生制举例_哔哩哔哩_bilibili 务实&#xff1a; 第一章 (1)会计概念&#xff0c;职能和目标&#xff1a; 2025年2月25日&#xff1a; (2)会计假设&#xff1a; 2025年2月26日&#xff1a; (3)会计核算基础&#xff1a; 202…

STM32——HAL库开发笔记22(定时器3—呼吸灯实验)(参考来源:b站铁头山羊)

本文利用前几节所学知识来实现一个呼吸灯实验&#xff1a;两颗led灯交替呼吸。 一、STM32CubeMX配置 step1&#xff1a;配置调试接口 step2&#xff1a;配置定时器 定时器1位于APB2总线上&#xff0c;如上图所示。 step3&#xff1a;配置时基单元 按照下图配置 时钟来源配置…

深度剖析数据中台架构图,铸造数字文明的基石

🔥🔥 AllData大数据产品是可定义数据中台,以数据平台为底座,以数据中台为桥梁,以机器学习平台为中层框架,以大模型应用为上游产品,提供全链路数字化解决方案。 ✨奥零数据科技官网:http://www.aolingdata.com ✨AllData开源项目:https://github.com/alldatacenter/a…

物联网通信应用案例之《智慧农业》

案例概述 在智慧农业方面&#xff0c;一般的应用场景为可以自动检测温度湿度等一系列环境情况并且可以自动做出相应的处理措施如简单的浇水和温度控制等&#xff0c;且数据情况可远程查看&#xff0c;以及用户可以实现远程控制。 基本实现原理 传感器通过串口将数据传递到Wi…

【蓝桥杯】每天一题,理解逻辑(1/90)【Leetcode 移动零】

文章目录 题目解析讲解算法原理【双指针算法思路】(数组下标充当指针)如何划分和执行过程大致 代码详情 题目解析 题目链接&#xff1a;https://leetcode.cn/problems/move-zeroes/description/ 题目意思解析 把所有的零移动到数组的末尾保持非零元素的相对顺序 理解了这两层…

DeepSeek R1满血+火山引擎详细教程

DeepSeek R1满血火山引擎详细教程 一、安装Cherry Studio。 Cherry Studio AI 是一款强大的多模型 AI 助手,支持 iOS、macOS 和 Windows 平台。可以快速切换多个先进的 LLM 模型,提升工作学习效率。下载地址 https://cherry-ai.com/ 认准官网&#xff0c;无强制注册。 这…

【框架】参考 Spring Security 安全框架设计出,轻量化高可扩展的身份认证与授权架构

关键字&#xff1a;AOP、JWT、自定义注解、责任链模式 一、Spring Security Spring Security 想必大家并不陌生&#xff0c;是 Spring 家族里的一个安全框架&#xff0c;特别完善&#xff0c;但学习成本比较大&#xff0c;不少开发者都觉得&#xff0c;这个框架“很重” 他的…

Idea2024中搭建JavaFX开发环境并创建运行项目

Idea2024中搭建JavaFX开发环境并创建运行项目 本文以Java语言为例演示如何创建JavaFX开发项目和部署开发环境&#xff0c;读者可以根据个人实际灵活选择相关参数。 一、项目创建与环境搭建步骤 新建JavaFX项目&#xff0c;选择适合项目实际的语言、系统和JDK。 项目设置-设置…

Skyeye 云智能制造办公系统 VUE 版本 v3.15.10 发布

Skyeye 云智能制造&#xff0c;采用 Springboot winUI 的低代码平台、移动端采用 UNI-APP。包含 30 多个应用模块、50 多种电子流程&#xff0c;CRM、PM、ERP、MES、ADM、EHR、笔记、知识库、项目、门店、商城、财务、多班次考勤、薪资、招聘、云售后、论坛、公告、问卷、报表…

Solr中得Core和Collection的作用和关系

Solr中得Core和Collection的作用和关系 一&#xff0c; 总结 在Apache Solr中&#xff0c;Core和Collection 是两个核心概念&#xff0c;他们分别用于单机模式和分布式模式&#xff08;SolrCloud&#xff09;中&#xff0c;用于管理和组织数据。 二&#xff0c;Core 定义&am…

【2025-02-26】基础算法:二分查找(二)

&#x1f4dd;前言说明&#xff1a; ●本专栏主要记录本人的基础算法学习以及LeetCode刷题记录&#xff0c;主要跟随B站博主灵茶山的视频进行学习&#xff0c;专栏中的每一篇文章对应B站博主灵茶山的一个视频 ●题目主要为B站视频内涉及的题目以及B站视频中提到的“课后作业”。…

Cherry Studio 使用/训练deepseek

Cherry Studio前言 CherryStudio 是一款集多模型对话、知识库管理、AI 绘画、翻译等功能于一体的全能 AI 助手平台。 CherryStudio的高度自定义的设计、强大的扩展能力和友好的用户体验&#xff0c;使其成为专业用户和 AI 爱好者的理想选择。无论是零基础用户还是开发者&#…

十、大数据资源平台功能架构

一、大数据资源平台的功能架构图总体结构 大数据资源平台功能架构图 关键组件&#xff1a; 1.用户&#xff08;顶行&#xff09; 此部分标识与平台交互的各种利益相关者。 其中包括&#xff1a; 市领导 各部门分析师 区政府 外部组织 公民 开发人员 运营经理 2.功能模…

UE Python笔记

插件 官方 商城 Python Editorhttps://www.fab.com/listings/f4c99ba0-1a86-4f6a-b19d-2fd13f15961b GitHUB 好像只更新到了2020年4.2x的版本。可能有大佬改了5.x的版本。也希望分享给我一份。谢谢 https://github.com/20tab/UnrealEnginePython 学习笔记 网上教程一大堆。…

SQL_优化

1 SQL优化 (1) 数据读取 ①分区裁剪:使用时只读取需要的分区. ②列裁剪:读取操作(select、where、join、group by、sort by等),不读取不需要的列,减少IO消耗. (2) 数据筛选 ①分区先过滤,区分度大的字段先过滤. ②不在筛选字段上使用函数和表达式. (3) 分组聚合 ①使用窗口函数…