直方图

直方图

灰度直方图

灰度图中的每个像素都是一个灰度值,占8位,取值为0~255。

灰度直方图能够很直观地展现出图像中各个灰度等级的分布情况。

灰度直方图的横坐标为各灰度等级(0~255),纵坐标是该灰度等级出现的像素个数。通常会将纵坐标归一化到[0,1]区间内,即将灰度等级出现的像素个数除以图像的像素总数,也就是说纵坐标表示该灰度等级出现的频率。

import cv2
import numpy as np
import matplotlib.pyplot as plt
# 加载一张灰度图
img = cv2.imread('flower.jpg', 0)
imgInfo = img.shape
height = imgInfo[0]
width = imgInfo[1]

GRAY_LV_NUM = 256
count = np.zeros(GRAY_LV_NUM, np.float32)

# 灰度等级的像素个数统计
for i in range(0, height):
    for j in range(0, width):
        index = int(img[i, j]) # 0~255
        count[index] = count[index] + 1
# 归一化为灰度等级出现的频率
imgSize = height * width
for i in range(0, GRAY_LV_NUM):
    count[i] = count[i] / imgSize
# 横坐标为灰度等级,纵坐标为灰度等级出现的频率  
x = np.linspace(0, GRAY_LV_NUM - 1, GRAY_LV_NUM)
y = count
# 灰度直方图展示
plt.figure()
plt.bar(x, y, 0.9, alpha=1, color='b')
plt.show()

cv2.imshow('src', img)
cv2.waitKey(0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

灰度图-w728

灰度直方图

彩色直方图

彩色图中的每个像素是由B、G、R三个颜色通道组成,而每个颜色通道占8位,取值为0~255。

彩色直方图是分别对彩色图的每个颜色通道进行统计分析,最终分别得到B直方图、G直方图和R直方图。而每个颜色通道的直方图的统计分析方法与上面的灰度直方图一样。

import cv2
import numpy as np
import matplotlib.pyplot as plt
# 加载一张彩色图
img = cv2.imread('flower.jpg', 1)
imgInfo = img.shape
height = imgInfo[0]
width = imgInfo[1]

COLOR_LV_NUM = 256

bCount = np.zeros(COLOR_LV_NUM, np.float32)
gCount = np.zeros(COLOR_LV_NUM, np.float32)
rCount = np.zeros(COLOR_LV_NUM, np.float32)
# 各个颜色通道的颜色等级的像素个数统计
for i in range(0, height):
    for j in range(0, width):
        (b, g, r) = img[i, j]
        bIndex = int(b)
        gIndex = int(g)
        rIndex = int(r)
        bCount[bIndex] = bCount[bIndex] + 1
        gCount[gIndex] = gCount[gIndex] + 1
        rCount[rIndex] = rCount[rIndex] + 1
# 归一化
imgSize = height * width
for i in range(0, COLOR_LV_NUM):
    bCount[i] = bCount[i] / imgSize
    gCount[i] = gCount[i] / imgSize
    rCount[i] = rCount[i] / imgSize

x = np.linspace(0, COLOR_LV_NUM - 1, COLOR_LV_NUM)
# B直方图
y = bCount
plt.figure()
plt.bar(x, y, 0.9, alpha=1, color='b')
# G直方图
y = gCount
plt.figure()
plt.bar(x, y, 0.9, alpha=1, color='g')
# R直方图
y = rCount
plt.figure()
plt.bar(x, y, 0.9, alpha=1, color='r')

plt.show()
cv2.waitKey(0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

彩色图

B直方图

G直方图

R直方图

直方图均衡化

假如图像的灰度分布不均匀,其灰度分布集中在较窄的范围内,会使图像的细节不够清晰,对比度较低。通常采用直方图均衡化,使图像的灰度更均匀地分布,从而增大反差,使图像细节清晰,对比度更高。

直方图均衡化是一种灰度的变换过程,它通过一个变换函数将当前的灰度分布变得范围更宽、分布更均匀。

一般选择灰度的累积概率作为直方图均衡化的变换函数,于是,直方图均衡化的算法步骤为:

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

$$P(S_k) = \frac{n_k}{n}, (n为像素总数, n_k为灰度等级S_k的像素个数)$$

  • 计算图像的累积直方图,即当前灰度等级k的概率为从0到k之间的灰度等级的概率的和。

$$CDF(S_k) = \sum\limits^k_{i=0}\frac{n_i}{n}=\sum\limits^k_{i=0}P(S_i)$$

  • 计算灰度等级对应的输出像素值,即当前灰度等级k的输出像素值为它的累积概率乘以最大灰度等级。

$$D_k = L\cdot CDF(S_k), (L为最大灰度等级,即255)$$

  • 创建灰度变化的映射表,灰度等级k映射到对应的输出像素值。利用该映射表,将图像变换为灰度均衡的图像。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 加载一张灰度图
img = cv2.imread('../res/flower0.jpg', 0)
imgInfo = img.shape
height = imgInfo[0]
width = imgInfo[1]

GRAY_LV_NUM = 256
count = np.zeros(GRAY_LV_NUM, np.float32)

# 灰度等级的像素个数统计
for i in range(0, height):
    for j in range(0, width):
        index = int(img[i, j])
        count[index] += 1
# 归一化:计算灰度等级出现的概率
imgSize = height * width
for i in range(0, GRAY_LV_NUM):
    count[i] = count[i] / imgSize
# 打印灰度直方图
x = np.linspace(0, GRAY_LV_NUM - 1, GRAY_LV_NUM)
y = count
plt.figure()
plt.bar(x, y, 0.9, alpha=1, color='black')
# 计算灰度等级的累积概率
total = float(0)
for i in range(0, GRAY_LV_NUM):
    total += count[i]
    count[i] = total
# 打印累积直方图
y = count
plt.figure()
plt.bar(x, y, 0.9, alpha=1, color='black')
# 创建灰度变化的映射表
m = np.zeros(GRAY_LV_NUM, np.uint16)
for i in range(0, GRAY_LV_NUM):
    m[i] = np.uint16(count[i] * (GRAY_LV_NUM - 1))
# 利用映射表,将图像变换为灰度均衡的图像
dst = np.zeros((height, width, 1), np.uint8)
for i in range(0, height):
    for j in range(0, width):
        index = int(img[i, j])
        dst[i, j] = m[index]

# 计算效果图的直方图,用于对比
count = np.zeros(GRAY_LV_NUM, np.float32)
for i in range(0, height):
    for j in range(0, width):
        index = int(dst[i, j])
        count[index] += 1
imgSize = height * width
for i in range(0, GRAY_LV_NUM):
    count[i] = count[i] / imgSize

y = count
plt.figure()
plt.bar(x, y, 0.9, alpha=1, color='black')

plt.show()
cv2.imshow('src', img)
cv2.imshow('dst', dst)
cv2.waitKey(0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

灰度图-w728

效果图-w725

原图的灰度直方图分布不均匀,集中在较窄的范围内。

灰度直方图

累积直方图对灰度直方图中的概率不断累积,越后面的灰度等级,其累积概率越将近1。

累积直方图

均衡化后的灰度直方图分布更均匀。

均衡化后的灰度直方图