资讯 小学 初中 高中 语言 会计职称 学历提升 法考 计算机考试 医护考试 建工考试 教育百科
栏目分类:
子分类:
返回
空麓网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
空麓网 > 计算机考试 > 软件开发 > 后端开发 > Python

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

Python 更新时间: 发布时间: 计算机考试归档 最新发布

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

文章目录

  • 直方图增强基本逻辑-均衡化
    • 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

    • 归一化到[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=1∑i​px​(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) ∑0S​H(S)就表示所有的概率之和。那么目标图的表示就是 ∑ 0 D H ( D ) sum_0^DH(D) ∑0D​H(D), 两者是相等的。可以表示为:
      ∑ 0 S H ( S ) = ∑ 0 D H ( D ) sum_0^SH(S) = sum_0^DH(D) 0∑S​H(S)=0∑D​H(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} 0∑S​H(S)=0∑D​H(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} 0∑S​H(S)=Nf(S)A​

      换一下项就可以得到:
      f ( S ) = N A ∑ 0 S H ( S ) f(S) = frac{N}{A}sum_0^SH(S) f(S)=AN​0∑S​H(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.konglu.com/
本文地址:http://www.konglu.com/it/1095163.html
免责声明:

我们致力于保护作者版权,注重分享,被刊用文章【python的opencv操作记录(13)-增强之直方图均衡化】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理,本文部分文字与图片资源来自于网络,转载此文是出于传递更多信息之目的,若有来源标注错误或侵犯了您的合法权益,请立即通知我们,情况属实,我们会第一时间予以删除,并同时向您表示歉意,谢谢!

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2023 成都空麓科技有限公司

ICP备案号:蜀ICP备2023000828号-2