python的opencv操作记录(13)-增强之直方图均衡化

news2024/11/25 22:24:37

文章目录

  • 直方图增强基本逻辑-均衡化
    • calcHist && equalizeHist
      • calcHist
      • equalizeHist
  • 自适应直方图均衡化

前段时间忙活深度网络和android的东西去了,好久没讲讲传统图像处理了,这一篇继续来说说opencv中的传统图像处理部分——图像增强之直方图增强。

图像增强是一种基本的图像处理操作,简单的来说就是把图像变的更清晰,或者说感兴趣的某个区域需要变的更加清晰。

而清晰度这个概念,在清晰度计算这一章节中提到过,一般来说,像素之间的梯度越大,图像就越清晰。

而直方图是用于统计像素分布的一个工具,计算每幅图像的直方图是传统图像处理中的一种基本操作,直方图表现了一张图像所有的像素的分布情况,直方图增强就是通过调整直方图的分布来实现图像的增强,简单的说就是把图像像素重新分布一下,提高图像中像素的整体梯度,让图像变得更清晰。

这个过程被称作直方图均衡化

附上之前直方图的基本计算方式:

https://blog.csdn.net/pcgamer/article/details/124989015?spm=1001.2014.3001.5501

清晰度计算:

https://blog.csdn.net/pcgamer/article/details/127942102?spm=1001.2014.3001.5501

直方图增强基本逻辑-均衡化

上面提到了均衡化的过程,其实就是把图像像素的分布改变一下。那么问题来了,怎么变?根据什么变?

首先来看一下opencv中的函数*equalizeHist()*的方式。

  • 首先说下累计分布函数CDF(cumulative distribution function),这个函数可以这么理解:

    • 直方图就是统计了某个灰度值的像素个数,比如灰度为100的像素个数有50个,总共有256中灰度值,那么可以记做:
      n 100 = 50 n_{100} = 50 n100=50
      ,或者是$ n_i = 100, i = 100, 0<i<255$

    • 归一化到[0, 1]的范围内,其实就是求这个灰度值出现的概率: p ( i ) = n i n p(i) = \frac{n_i}{n} p(i)=nni,n为像素总数。

    • 那么累计分布函数就是:
      H ( x ) = ∑ j = 1 i p x ( j ) H(x) = \sum_{j=1}^ip_x(j) H(x)=j=1ipx(j)
      也就是某个灰度值所有的累计分布,比如灰度值100的H(x)就是从0-100的所有灰度值概率分布之和。

    • 通过把每个点的像素值通过 H ( x ) H(x) H(x)来进行转换获得新的目标图像的灰度值。

  • 有意思的是为什么要这么进行转换,这个证明过程不复杂,可以简单列一列。

    • 首先可以认为原始图S,和目标图D。

    • 原始图S的直方图分布记做 H A ( S ) H_A(S) HA(S),目标图D的直方图分布记做 H B ( D ) H_B(D) HB(D)

    • 我们的目的就是要找到一个映射关系 f f f,可以把原始图S中的像素值映射到目标图D,也就是说 D = f ( S ) D = f(S) D=f(S)

    • 直方图归一化到[0, 1]后,实际上就是某个灰度值的概率,所有的概率之和都是等于。

    • 对于原图的直方图表示: ∑ 0 S H ( S ) \sum_0^SH(S) 0SH(S)就表示所有的概率之和。那么目标图的表示就是 ∑ 0 D H ( D ) \sum_0^DH(D) 0DH(D), 两者是相等的。可以表示为:
      ∑ 0 S H ( S ) = ∑ 0 D H ( D ) \sum_0^SH(S) = \sum_0^DH(D) 0SH(S)=0DH(D)

    • 最理想的目标图分布是均匀分布,也就是 H ( D ) = A N H(D) = \frac{A}{N} H(D)=NA, 其中的A表示每种像素值的值(每种都是相同的)。那么上面的公式就可以写成:
      ∑ 0 S H ( S ) = ∑ 0 D H ( D ) = D A N \sum_0^SH(S) = \sum_0^DH(D) = \frac{DA}{N} 0SH(S)=0DH(D)=NDA
      其中 D = f ( S ) D=f(S) D=f(S)
      所有有可以写成:
      ∑ 0 S H ( S ) = f ( S ) A N \sum_0^SH(S) = \frac{f(S)A}{N} 0SH(S)=Nf(S)A

      换一下项就可以得到:
      f ( S ) = N A ∑ 0 S H ( S ) f(S) = \frac{N}{A}\sum_0^SH(S) f(S)=AN0SH(S)

      最右边的那一坨中的H(S)就是上面提到的累积概率分布,只是这里要对整张图像的像素再做一次求和或者积分。

    • 具体怎么计算,这里就不说了,有兴趣的朋友可以去了解下。

    • 当然,这里有个问题,上面的理想状态是不太可能达到的,如果某些图像的直方图在某个小区域出现比较大的聚集的话,可能就没法非常好的进行平均分布了。

calcHist && equalizeHist

在opencv中,用于直方图均衡化的函数就是equalizeHist:

先上代码:

  img = cv2.imread("xxxx")

  img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

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

  plt.title('Gray Histogram Contour')
  plt.xlabel('gray level')
  plt.ylabel('number of pixels')

  plt.figure(1)
  plt.plot(hist)

  dst = cv2.equalizeHist(img)

  hist_new = cv2.calcHist([dst], [0], None, [256], [0, 255])

  plt.title('Gray Histogram Contour new')
  plt.xlabel('gray level')
  plt.ylabel('number of pixels')

  plt.figure(2)
  plt.plot(hist_new)

  plt.show()

  cv2.imshow("src", img)
  cv2.imshow("new", dst)

  cv2.waitKey()
  cv2.destroyAllWindows()

calcHist

在调用均衡化之前,先要计算图像的直方图,用于后续进行对比实验。
计算图像直方图的函数:调用opencv中的calcHist函数:

  • calcHist函数接受下面几个参数
  • [img],以列表的形式作为参数,img为需要计算直方图的图像。
  • [0],通道数,如果是灰度图,就传0
  • None,mask,用于ROI的掩码,传None就是统计整张图像。
  • [256],histSize,就是说分成多少类,如果全部统计的话就是256个类。
  • [0, 255],就是说哪些像素值需要被统计,[0, 255]就表示所有的像素值都需要被统计(8位)

然后再通过plt库进行绘图展示。

equalizeHist

直方图均衡化,这个函数就相对比较简单了,直接从源图到目标图进行转换。

我们看一下上面代码的结果,用来处理经典的一张图:

  • 源图:
    在这里插入图片描述

  • 源图直方图
    在这里插入图片描述

  • 目标图
    在这里插入图片描述

  • 目标图直方图
    在这里插入图片描述

从直方图分布可以看出,均衡化已经将像素点从相对集中变成了相对平衡的分布了。而这个累积分布概率函数的转换就是表明希望通过这样一个转换,使得像素尽量去满足一个像素值平均分布。
从最终形成的图像上来看,也是把一幅雾蒙蒙的图像变得相对清晰了。
但是存在一个这个方法典型的确定,马赛克现象比较严重。

自适应直方图均衡化

上面提到的均衡化方法有两个比较明显的不足:

  1. 马赛克现象,我理解是因为灰度是一个离散的点,会造成某个小区域发生阶跃性的变化,造成这种现象。
  2. 噪声被放大的现象,因为在整张图像上把像素值拉平的原因。

进一步的一个算法就是自适应直方图均衡化。
简单来说就是在上面的算法上做了两点改动:

  1. 利用局部特征,或者说局部的ROI特征进行CDF变换。也就是某个像素点周边的一个W*W的区域。这样就可以让灰度的阶跃变小。因为在一个小的区域里变换,噪声被放大的影响也不会太大。
  2. 上一步的改进中,会造成区域与区域之间被认为造成一些“边界”,所以这些边界需要通过双线性插值来进行“模糊”,让图像过渡比较连续。(双线性插值可以参考https://blog.csdn.net/pcgamer/article/details/125426351?spm=1001.2014.3001.5502)
  3. 为了防止局部对比度过于夸张,增加了一个限制对比度的参数,如果超过这个阈值,则会通过某种规则把这些灰度值分摊到区域中的其他像素值上去,让整个局部直方图更加的平缓。

代码也挺简单:

    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
    cl1 = clahe.apply(img)

    hist_cl1 = cv2.calcHist([cl1], [0], None, [256], [0, 255])

    plt.title('Gray Histogram Contour hist_cl1')
    plt.xlabel('gray level')
    plt.ylabel('number of pixels')

    plt.figure(3)
    plt.plot(hist_cl1)

    plt.show()

    cv2.imshow("hist_cl1", cl1)

其中的方法cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))是创建了一个CLANE类,其中的两个参数

  • clipLimit = 2.0,对比度限制这个参数是用每块的直方图的每个bins的数和整图的平均灰度分布数的比值来限制的。 裁剪则是将每块图像直方图中超过ClipLimit的bins多出的灰度像素数去除超出部分,然后将所有bins超出的像素数累加后平均分配到所有bins。具体怎么分配的就不是特别清楚了。
  • tileGridSize就是每个小区域的大小
  • 再通过apply应用到图像上去
  • CLANE是opencv算法库中的一种。
    在这里插入图片描述

在这里插入图片描述

相比之前算法的图,有两点改进:

  1. 对比度与源图更类似,而不是整体上改变了源图的对比度,相对于普通的均衡化方法,分布的不是那么的均匀,但是更好的代表了源图的某些特征。
  2. 马赛克现象得到了缓解。

当然,不是在所有的图像上,自适应方法都可以比普通的均衡化方法更好的,需要根据图像的特征来进行判断。``

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

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

相关文章

【谷粒商城之订单服务-RabbitMQ延时队列】

本笔记内容为尚硅谷谷粒商城订单服务锁库存事务最终一致性部分 目录 一、RabbitMQ延时队列 二、具体实现 1、 创建上述队列和路由组件 2、解锁库存 解锁库存的两种情况 1、当订单业务提交后回滚 2、订单取消解锁库存 三、关闭订单 四、消息丢失、挤压、重复等解决方案…

c++ 继承与派生

继承就是在原有类的基础上产生新类&#xff0c;从而减少了代码重复的必要 格式&#xff1a; class A{}; class B:继承方式 A{}; 在c继承中&#xff0c;有以下几点注意 (1)基类的构造函数与析构函数不能被继承 (2)派生类对基类成员的继承没有选择权&#xff0c;不能选择继承…

c++ 常用总结(三)

1.设计模式 GitHub - FengJungle/DesignPattern: Design pattern demo code &#xff08;一&#xff09; ① 简单工厂模式 再不学简单工厂模式&#xff0c;就真的要去工厂搬砖啦~_冯Jungle的博客-CSDN博客 通过以下的例子可见&#xff0c;只需要提供产品名称作为参数&…

基于springboot+mysql+SpringDataJPA +html实现学生选课管理系统

基于springbootmysqlSpringDataJPA html实现学生选课管理系统 一、系统介绍1、系统主要功能&#xff1a;2.涉及技术框架&#xff1a;3.本项目所用环境&#xff1a;4.项目需求 二、功能展示三、其它系统四、获取源码 一、系统介绍 1、系统主要功能&#xff1a; 管理员&#xf…

【C++】| 03——STL | 迭代器

系列文章目录 【C】| 01——泛型编程 | 模板 【C】| 02——STL | 初识 【C】| 03——STL | 迭代器 【C】| 04——STL | 容器_vector 文章目录 1. 什么是迭代器2. 迭代器的分类3. 不同容器对应的迭代器4. 迭代器的好处5. 迭代器的操作 1. 什么是迭代器 迭代器就是指向容器内元素…

基于MATLAB的路面裂缝检测识别算法代码(GUI系统设计+图像预处理+裂缝检测)

资源地址&#xff1a; 基于MATLAB的路面裂缝检测识别算法代码&#xff08;GUI系统设计图像预处理裂缝检测&#xff09;资源-CSDN文库 主要内容&#xff1a; 1、运行Gui_Main.m程序&#xff0c;得到GUI界面 2、首先点击载入图像文件 3、后续便可以点击右侧的其他按钮进行分析…

C++linux高并发服务器项目实践 day10

Clinux高并发服务器项目实践 day10 守护进程进程组会话进程组、会话操作函数守护进程守护进程的创建步骤 线程线程和进程的区别线程之间共享和非共享资源线程操作线程创建线程退出线程参与线程分离线程取消 线程属性 守护进程 在UNIX系统中&#xff0c;用户通过终端登录系统后…

DCMM评估之战略维度沟通

01 数据战略规划过程 过程描述&#xff1a; 过程描述如下:a) 识别利益相关者,明确利益相关者的需求;b) 数据战略需求评估,组织对业务和信息化现状进行评估,了解业务和信息化对数据的需求;c) 数据战略制定,包含但不限于:1) 愿景陈述,其中包含数据管理原则、目的和目标;2) 规划…

SpringCloud 远程调用Feign、网关Gateway、配置中心Nacos、微服务架构小结、Nacos搭建集群

统一检查maven maven依赖出错的解决 注意代码格式化。因代码格式混乱&#xff0c;导致代码出错&#xff0c;pom.xml出现重复的parent标签 学习方法&#xff0c;听得懂为什么要这么做&#xff0c;要远远比 怎么做 重要的多 一、远程调用Feign 能够使用Feign进行远程调用能够…

【C++学习】创建二维动态数组

1.指针 创建二维动态数组_牛客题霸_牛客网 (nowcoder.com) 使用指针的指针 使用指针的指针可以很方便地创建动态的二维数组&#xff0c;其关键在于使用两层指针进行分配。 以下是一个动态创建n行m列的二维数组的示例代码&#xff1a; int **arr new int*[n]; // 创建一个…

深度学习训练营J2:ResNet50v2算法分析与实战

深度学习训练营J2:ResNet50v2算法分析与实战 原文链接环境介绍0.引言论文分析与解读1.ResNet50和ResNet50v2之间的结构对比2.不同结构之间的尝试 3.关于激活的不同尝试4.文章结果 ResNet50v2架构复现5.残差结构6.模块构建7.架构展示以及网络构建 8.网络结构打印ResNet50v2完整结…

Python——1

一、注释 &#xff08;1&#xff09;单行注释&#xff1a;#需要注释的内容&#xff08;#&#xff09; &#xff08;2&#xff09;多行注释&#xff1a;需要注释的内容&#xff08;三引号&#xff09; 二、变量及变量类型 1.变量 语法定义&#xff1a;变量名 变量值&#…

【小程序】微信云托管服务

链接 官方文档 云托管官网 特点 无需自提供服务&#xff0c;有云托管平台自动分配&#xff0c;并自动缩容/扩容支持多种语言及模板实例采用容器化管理方式实现服务部署支持小程序内网访问&#xff0c;仅公网测试&#xff0c;提供足够的安全防护&#xff0c;微信用户就近接入…

Python:BeautifulSoup库介绍

BeautifulSoup库介绍 1、BeautifulSoup是Python中的一个第三方库&#xff0c;其最主要的功能是处理HTML文档 ⑴查找HTML文档中的指定标签 ⑵获取HTML文档中指定标签的标签名、标签值、标签属性等 ⑶修改HTML文档中指定标签 2、BeautifulSoup库将HTML文档解析为一…

服务器如何做端口映射,使服务器之间通信,然后访问目标网站(baidu.com)

文章目录 服务器如何做端口映射&#xff0c;使服务器之间通信&#xff0c;然后访问目标网站&#xff08;baidu.com)问题缘由所需环境操作步骤1. 目的服务器设置2. 中间服务器设置3. 修改客户端 总结 服务器如何做端口映射&#xff0c;使服务器之间通信&#xff0c;然后访问目标…

DataX读取Hive Orc格式表丢失数据处理记录

文章目录 问题问题概述问题详细描述 原因解决方法修改源码验证 问题 问题概述 DataX读取Hive Orc存储格式表数据丢失 问题详细描述 同步Hive表将数据发送到Kafka&#xff0c;Hive表A数据总量如下 SQL&#xff1a;select count(1) from A; 数量&#xff1a;19397281使用Dat…

HTML小游戏25 —— HTML5拉杆子过关小游戏(附完整源码)

本节教程我会带大家使用 HTML 、CSS和 JS 来制作一个HTML5拉杆子过关小游戏 ✨ 前言 &#x1f579;️ 本文已收录于&#x1f396;️100个HTML小游戏专栏&#xff1a;100个H5游戏专栏https://blog.csdn.net/qq_53544522/category_12064846.html&#x1f3ae; 目前已有100小游戏…

交叉编译--build、--host、--target、--prefix

一、编译例子 ./configure --build编译平台 --host运行平台 --target目标平台 [各种编译参数]build&#xff1a;表示目前我们正在运行的平台名称是什么&#xff0c;如&#xff1a;当前我们是在电脑上编译该系统&#xff0c;那么我们的 --build 就可能是 x86&#xff0c;如果…

如何避免因为 Kubernetes 和 Kafka 而被解雇

本文由 Bing AI 生成。Bing AI 真是尽显程序员本色&#xff0c;我等它生成文章的过程中发现出现了 Markdown 语法&#xff0c;结果点复制过来的就是直接 Markdown 文档。 Kubernetes 和 Kafka 是两个非常流行的技术&#xff0c;它们分别用于容器编排和分布式消息传递。它们的优…

XSD2Code++ Crack

XSD2Code Crack XSD2Code是为那些希望在将复杂的XML和JSON模式转换为NetCore时节省时间的开发人员设计的。它使用简单且灵活&#xff0c;可以很容易地集成到任何项目中&#xff0c;并适应开发人员的需求。它通过直观、可定制的用户界面&#xff0c;真正提高了生产力。使用XSD2C…