颜色直方图均衡化

发布时间 2023-09-04 00:01:04作者: wancy

1.颜色空间

  色彩是人的眼睛对于不同频率的光线的不同感受,色彩既是客观存在的(不同频率的光)又是主观感知的,有认识差异。所以人类对于色彩的认识经历了极为漫长的过程,直到近代才逐步完善起来,但人类仍不能说对色彩完全了解并准确表述了,许多概念不是那么容易理解。色彩模型是描述使用一组值(通常使用三个、四个值或者颜色成分)表示颜色方法的抽象数学模型。例如 三原色光模式(RGB) 和印刷四分(CMYK) 都是色彩模型。但是一个与绝对色彩空间没有函数映射关系的色彩模型或多或少地都是与特定应用要求几乎没有关系的任意色彩系统。

  1. RGB色彩空间:RGB(Red, Green, Blue)是一种加色模式,通过调节红、绿、蓝三个原色的亮度和混合比例来表示不同的颜色。

  2. CMYK色彩空间:CMYK(Cyan, Magenta, Yellow, Key)是一种减色模式,通常用于印刷领域,通过调节青、品红、黄三个色料的浓度和黑色的亮度来表示不同的颜色。

  3. HSV色彩空间:HSV(Hue, Saturation, Value)是一种颜色模型,通过色相、饱和度和亮度三个属性来描述颜色。色相表示颜色的基本属性,饱和度表示颜色的纯度和鲜艳程度,亮度表示颜色的明亮程度。

  4. YUV色彩空间:YUV是一种亮度和颜色分量分离的颜色模型,Y表示亮度分量,U和V表示色度分量。在视频编码和传输领域广泛应用。

  5. Lab色彩空间:Lab色彩空间是一种基于人眼视觉主观感受的颜色模型,L表示亮度,a和b分别表示红绿和黄蓝两个色差分量。Lab色彩空间可以提供更好的色彩鲜艳性和色彩变化均匀性。

  下图是RGB与HSV的色彩空间模型。

                                                                                 

2. 颜色直方图

  颜色直方图(Color Histogram)是一种描述图像颜色分布的统计工具。它可以通过将图像中每个像素的颜色值分离为红色、绿色和蓝色三个通道,然后统计每个通道中颜色值的频率,得到一个直方图。颜色直方图是在许多图像检索系统中被广泛采用的颜色特征。它所描述的是不同色彩在整幅图像中所占的比例,而并不关心每种色彩所处的空间位置,即无法描述图像中的对象或物体。颜色直方图特别适于描述那些难以进行自动分割的图像。颜色直方图分为全局颜色直方图与累加颜色直方图。颜色直方图分为全局直方图与累加颜色直方图。

  全局直方图: 全局直方图表示了图像中所有像素的颜色分布情况。它可以通过统计每个颜色通道的像素值出现频次来计算。全局直方图常用于图像检索、图像分类以及颜色特征分析等应用。

  累加颜色直方图: 累加颜色直方图是对全局直方图进行累加操作得到的直方图。在累加过程中,将每个像素的值累加到前一个像素的频次上,最终得到的直方图反映了像素值在图像不同位置的分布情况。通过分析累加颜色直方图,可以了解图像中颜色随位置的变化,例如在红色、蓝色等颜色区域的分布情况。

  本文只对全局颜色直方图进行实现。下面绘制了某个图像的灰度直方图。代码如下:

'''
灰度图像的直方图
'''
import cv2
import matplotlib.pyplot as plt

img=cv2.imread('../data/sunrise.jpg',0)#0灰度,默认为1,彩色图
cv2.imshow('img',img)
#直方图
plt.hist(img.ravel(),bins=256,range=(0,256))#hist只能接受一维数据
plt.show()
cv2.waitKey()
cv2.destroyAllWindows()

   上面的下图为灰度图(单通道,0-255范围)。上面的蓝色柱状图横坐标为灰度图的像素值的大小,纵坐标为该图中每个灰度级(每个像素值)所拥有的的像素值得个数。如果想要根据灰度值的颜色显示对应的直方图颜色,可以如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams

rcParams['font.sans-serif'] = ['SimHei']  # 设置字体为SimHei
# 读取图像
image = cv2.imread('../data/sunrise.jpg', 0)  # 以灰度模式读取
# 计算直方图
hist, bins = np.histogram(image.flatten(), bins=256, range=[0, 256])
h=bins[:-1]/255.0
# 创建颜色映射
colors=plt.cm.gray(h)
# 绘制直方图
plt.bar(bins[:-1], hist, width=2, color=colors)
plt.xlabel('像素值')
plt.ylabel('像素个数')
# plt.colorbar(colors=colors)#报错
# 显示图像
plt.show()

3.累计分布函数(CDF

  直方图的累计分布函数 (CDF) 是表示在给定的灰度级别下,像素值小于等于该灰度级别的概率。

  在直方图中,每个灰度级别有对应的像素数量或频数。CDF 使用这些频数来计算像素在低于或等于给定灰度级别的所有灰度级别上的概率。

  具体计算过程如下:

  1. 首先,对直方图进行归一化处理,将频数转换为像素概率。这可以通过将每个频数除以图像总像素数来实现。
  2. 然后,计算归一化后的直方图的累积和。从灰度级别最小的开始,逐级将当前概率值与前一个灰度级别的概率值进行相加,得到当前累积概率值。这样,每个灰度级别都会有一个累积该概率
  3. 最后,累计分布函数(CDF)就是每个灰度级别的累积概率。

  代码如下:

'''
累计分布函数
'''
import cv2
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import rcParams

rcParams['font.sans-serif'] = ['SimHei']  # 设置字体为SimHei
# 读取灰度图像
img = cv2.imread('../data/sunrise.jpg', cv2.IMREAD_GRAYSCALE)
size=img.shape#(330, 500)
# 计算直方图
hist, bins = np.histogram(img.flatten(), 256, [0, 256])
cdf = hist.cumsum()
cdf=cdf/(size[0]*size[1])#除以像素总和# 绘制直方图
fig, ax = plt.subplots(figsize=(10, 6))
# ax.bar(bins[:-1], cdf, width=np.diff(bins), color=plt.cm.gray(cdf / cdf.max()))
ax.bar(bins[:-1], cdf, width=np.diff(bins))
ax.set_xlabel('灰度级别')
ax.set_ylabel('CDF')
ax.set_title('灰度直方图')
plt.show()

  上图中,随着灰度级别变大,CDF值逐渐变大,直到最大值为1。在此基础上进行均衡化,了解 均衡化之前,还需要了解均匀分布的累积分布函数,将上面的累计分布函数图转换为均匀分布的累计分布函数图后,就实现了颜色直方图均衡化。

   如上图所示,随着灰度级别增加,累计分布值逐渐变大,当灰度级别为255。而且是函数值线性增加。我们需要做的是,在保持像素个数不变的情况下,改变灰度级,且保证每个灰度级所占比例不变。也就是如下图这样:

   观察上图可以发现,将曲线中原始灰度图像在[0,灰度级]范围内的像素个数占总数的比例(也就是CDF)的所有绿色点(曲线上的所有绿色点,我这里只画了几个)保持CDF值不变的情况下,平移到红色直线上,也就是改变灰度级。这样就实现了图像的均衡化,图像暗的部分会变亮,亮的部分会得到抑制。比如图中的最下面的绿色点变换后,就是将原图中灰度值为45的所有像素变为会灰度值为105的像素。下面给出均衡化后的CDF图与代码。

  代码:

import cv2
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import rcParams
rcParams['font.sans-serif'] = ['SimHei']  # 设置字体为SimHei

# 读取经过均衡化后的灰度图像
img = cv2.imread('new.png', cv2.IMREAD_GRAYSCALE)
size=img.shape
#(330, 500)
# 计算直方图
hist, bins = np.histogram(img.flatten(), 256, [0, 256])
cdf = hist.cumsum()
print(len(cdf))
cdf=cdf/(size[0]*size[1])#除以像素总和
h=bins[:-1]/255.0
colors=plt.cm.gray(h)
# 绘制直方图
fig, ax = plt.subplots(figsize=(10, 6))
ax.bar(bins[:-1], cdf, width=np.diff(bins),color=colors)
ax.set_xlabel('灰度级别')
ax.set_ylabel('CDF')
ax.set_title('累计分布函数图')
plt.show()

4. 灰度图像直方图均衡化

  直方图均衡化是图像处理领域中利用图像直方图对对比度进行调整的方法。通过这种方法,亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度,直方图均衡化通过有效地扩展常用的亮度来实现这种功能。

  这种方法对于背景和前景都太亮或者太暗的图像非常有用,这种方法尤其是可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。这种方法的一个主要优势是它是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大。这种方法的一个缺点是它对处理的数据不加选择,它可能会增加背景噪声的对比度并且降低有用信号的对比度。

  灰度图均衡化,以上面的图片为例。

'''
灰度图像的直方图均衡化
'''
import cv2
import matplotlib.pyplot as plt

img=cv2.imread('../data/sunrise.jpg',0)#0灰度,默认为1,彩色图
cv2.imshow('img',img)
#直方图
plt.subplot(2,1,1)
plt.hist(img.ravel(),bins=256,range=(0,256))#hist只能接受一维数据
#均衡化
res=cv2.equalizeHist(img)
cv2.imshow('res',res)
plt.subplot(2,1,2)
plt.hist(res.ravel(),bins=256,range=(0,256))
plt.show()
cv2.waitKey()
cv2.destroyAllWindows()

  灰度直方图像素值范围分布变均匀了。

 变化后的图如上图右图所示,其对应的灰度直方图(与上面蓝色的一致,但是使用了灰色表示)如下:

  这里先将均衡化后的numpy数组保存为图像,在上面的代码中添加一行代码:

  cv2.imwrite('new.jpg',res)

  再重新读取保存的图片,绘制直方图:

import cv2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['font.sans-serif'] = ['SimHei']  # 设置字体为SimHei
# 读取图像
image = cv2.imread('new.jpg', 0)  # 以灰度模式读取
print(image)

# 计算直方图
hist, bins = np.histogram(image.flatten(), bins=256, range=[0, 256])
print(hist)
h=bins[:-1]/255.0
# 创建颜色映射
# colors = plt.cm.viridis(hist)  # 选择一个颜色映射,这里使用viridis
colors=plt.cm.gray(h)
# 绘制直方图
# plt.bar(bins[:-1], hist, width=1, color=colors)
plt.bar(bins[:-1], hist, width=1)
plt.xlabel('像素值')
plt.ylabel('像素个数')
# plt.colorbar(colors=colors)#报错
# 显示图像
plt.show()

  绘制直方图如下:

   仔细观察,发现直方图居然发生了变化,与前面的比较,前面的中间有很多空隙。

  为什么会这样呢?我们仅仅是将均衡化后的图像数组使用cv2.imwrite('new.jpg',res)进行了保存。再次读取显示直方图却发生了变化。其原因是jpg格式采用了压缩算法,如果想要保存图像不发生变化,可以使用png无损格式。所9以可以改为:

  cv2.imwrite('new.png',res),再次读取显示直方图:

5. 彩色图像亮度直方图均衡化  

  方法1.BGR颜色空间下进行直方图均衡化,可以分别对每个通道进行均衡化。以下是使用OpenCV库实现彩色图像直方图均衡化(在BGR颜色空间)的代码:

import cv2

# 读取彩色图像
img = cv2.imread('sunrise.jpg')
# 分离BGR图像的通道
b, g, r = cv2.split(img)
# 对每个通道进行直方图均衡化
equ_b = cv2.equalizeHist(b)
equ_g = cv2.equalizeHist(g)
equ_r = cv2.equalizeHist(r)
equ_img = cv2.merge((equ_b, equ_g, equ_r))#合并均衡化后的通道
# 显示均衡化前后的彩色图像
cv2.imshow('Original Image', img)
cv2.imshow('Equalized Image', equ_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

  以上代码读取一张彩色图像,将其分离为B、G、R三个通道,分别对每个通道进行直方图均衡化,然后将均衡化后的通道合并为一张彩色图像,最终显示均衡化前后的彩色图像。

 

 

  方法2.将彩色图像转换为HSV色彩空间。

  1. HSV图像上仅对亮度(Value)通道进行直方图均衡化。
  2. 将均衡化后的亮度通道与原始饱和度(Saturation)和色调(Hue)通道组合,得到均衡化后的HSV图像。
  3. 将均衡化后的HSV图像转换回BGR色彩空间,得到最终的均衡化彩色图像。

  代码:

 

import cv2
img=cv2.imread('sunrise.jpg')
cv2.imshow('img',img)
#BGR-->HSV
hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
cv2.imshow('hsv',hsv)
v=hsv[:,:,2]
hsv[:,:,2]=cv2.equalizeHist(v)#v均衡化后赋值给原始数据的v亮度
# cv2.imshow('res',hsv)#imshow要按BGR显示
#HSV-->BGR
res=cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)
cv2.imshow('res',res)
cv2.waitKey()
cv2.destroyAllWindows()

 

  上述代码中先将BGR颜色空间转换为HSV颜色空间,然后对V通道实现均衡化,就能达到变亮的效果。与BGR三个通道分别均衡化效果不同。

   方法3:将bGR彩色图像转换为YUV色彩空间,对亮度通道Y进行直方图均衡化

import cv2
# 读取彩色图像
img = cv2.imread('sunrise.jpg')
# 将图像转换为YUV色彩空间
yuv_img = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
# 分离YUV图像的通道
y, u, v = cv2.split(yuv_img)
# 对亮度通道进行直方图均衡化
equ_y = cv2.equalizeHist(y)
# 合并均衡化后的亮度通道和原始色度通道
equ_yuv_img = cv2.merge((equ_y, u, v))
# 将均衡化后的YUV图像转换回BGR色彩空间
equ_color_img = cv2.cvtColor(equ_yuv_img, cv2.COLOR_YUV2BGR)
# 显示均衡化前后的彩色图像
cv2.imshow('Original Image', img)
cv2.imshow('Equalized Image', equ_color_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

  以上代码读取一张彩色图像,将其转换为YUV色彩空间,在Y通道上进行直方图均衡化,然后将均衡化后的YUV图像转换回BGR色彩空间,最终显示均衡化前后的彩色图像。

6.优点与缺点:

颜色直方图均衡化有以下好处:

  1. 增强图像对比度:通过调整图像的亮度级别,颜色直方图均衡化可以增强图像的对比度,使得图像中的细节更加清晰可见。

  2. 扩展动态范围:颜色直方图均衡化可以将图像的像素值均匀分布在整个亮度范围内,从而扩展图像的动态范围,使得图像中的细节更加丰富。

  3. 增强图像细节:颜色直方图均衡化可以调整图像的亮度分布,使得原本暗淡或过亮的区域变得更加明亮或更加暗淡,突出图像中的细节和特征。

  4. 提高图像的视觉质量:通过增强对比度和细节,颜色直方图均衡化可以改善图像的视觉质量,使得图像更加饱满、生动和有吸引力。

然而,颜色直方图均衡化也存在一些缺点:

  1. 过度增强:在某些情况下,颜色直方图均衡化可能会过度增强图像中的某些细节,导致图像出现过分亮或过分暗的区域,从而产生不自然的效果。

  2. 处理时间长:颜色直方图均衡化需要对图像进行复杂的计算,因此对于大尺寸的图像,处理时间可能较长,影响实时性要求。

  3. 适应性差:颜色直方图均衡化是对整个图像进行处理,不能考虑图像中的局部特征和背景信息,因此在某些情况下,效果可能不理想。

综上所述,颜色直方图均衡化在增强图像对比度和亮度、扩展动态范围以及提高图像视觉质量方面具有一定的优势,但也需要根据具体情况权衡利弊,并考虑其可能带来的缺点。

 

   小结:将numpy数组保存为jpg格式时,数组图像会被压缩,图像会失真,可以使用png无损格式保存图像。均衡化会使得暗的部分变得明亮,亮的部分得到抑制。

 参考资料:

https://baike.baidu.com/item/%E7%9B%B4%E6%96%B9%E5%9B%BE%E5%9D%87%E8%A1%A1/4950487?fr=ge_ala

https://zhuanlan.zhihu.com/p/266528081?utm_source=QQ_article_bottom

https://blog.csdn.net/zxdd2018/article/details/130117224

https://blog.csdn.net/weixin_45930877/article/details/119581282

http://www.eepw.com.cn/zhuanlan/211673.html

https://www.cnblogs.com/sumuncle/p/5619259.html

https://blog.51cto.com/u_13984132/5558481

https://zhuanlan.zhihu.com/p/550243036

https://baijiahao.baidu.com/s?id=1719410535874372734&wfr=spider&for=pc

https://www.sohu.com/a/463106409_120124046

https://baike.baidu.com/item/%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4/4615427?fr=ge_ala

 

  不足或错误之处,欢迎指出或评论!