数字图像处理之阈值分割

1.直方图阈值分割

算法描述:根据图像灰度直方图,人工寻找阈值

算法特点:适用于双峰直方图

image.png

代码

1
2
3
4
5
6
7
8
9
10
11
plt.hist(img.ravel(), 256, [0, 256])
plt.show()

plt.hist(img.flatten(), np.arange(-0.5, 256, 1), color='g')
plt.show()

_, img_bin = cv.threshold(img, 125, 255, cv.THRESH_BINARY) #二值化
_, img_bin = cv.threshold(img, 125, 255, cv.THRESH_BINARY_INV) #inverse
show(img_bin)
#使用白色背景inverse
show(img_bin)

2.三角法阈值分割

算法思想:几何法,适用单峰图像

image.png

1
2
3
th, img_bin = cv.threshold(img, 0, 255, cv.THRESH_TRIANGLE)
print(th) #自动找到的阈值分割点
show(np.hstack([img, img_bin]))

3.迭代法阈值分割

算法步骤:

  1. 选取初始分割阈值,通常可选取图像灰度平均值T
  2. 根据阈值T将图像像素分割为前景和背景,分别求出两者平均灰度T~0~和T~1~
  3. 计算新域值T` = (T~0~+T~1~) /2
  4. 若T==T’,则迭代结束,T为最终值。否则令T == T’ 转第(2)步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
img = cv.imread('pic/eagle500x500.jpg', 0)

T = img.mean() #平均值

while True:
t0 = img[img < T].mean() #小于T的平均值
t1 = img[img >= T].mean() #大于T的平均值
t = (t0 + t1) / 2 #计算新阈值
if abs(T - t) < 1: #相差小于1即可
break
T = t
T = int(T) #最佳阈值

print(f"Best threshold = {T}")

4.大津法阈值分割

算法思想:最大类间方差法

对于初始阈值T,可以将图像分为目标和背景,其中背景占图像比例为p0,平均值为m0,而目标点数占图像比例为p1,平均值为m1

类间方差为:

带入p~0~+p~1~=1

遍历灰度值,找出能使σ最大的值

1
2
3
4
#openCV函数
th, img_bin = cv.threshold(img, -1, 255, cv.THRESH_OTSU)
print(th)
show(img_bin)

代码复现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
img = cv.imread('pic/eagle500x500.jpg', 0)

Sigma = -1
T = 0

for t in range(0, 256):
bg = img[img <= t]
obj = img[img > t]

p0 = bg.size / img.size
p1 = obj.size / img.size

m0 = 0 if bg.size == 0 else bg.mean()
m1 = 0 if obj.size == 0 else obj.mean()

sigma = p0 * p1 * (m0 - m1)**2

if sigma > Sigma:
Sigma = sigma
T = t

print(f"Best threshold = {T}")

5.自适应阈值分割

算法思想:局部二值化

全局二值化容易受阴影影响,所以可以使用局部二值化

  1. 对某像素值,原来为S,取其周围nxn的区域,求区域均值或高斯加权值,记为T
  2. 如果S>T,则该点为255,否则为0
1
2
3
img_bin = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY, 21, 6)
#(图像,二值化结果,阈值化方法取平均值,二值化方式,窗口大小,常数C )
show(img_bin)

算法复现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#使用均值
img = cv.imread('pic/page760x900.jpg', 0)

C = 6
winSize = 21

img_blur = cv.blur(img, (winSize, winSize))
img_bin = np.uint8(img > img_blur.astype(np.int) - C) * 255

show(img_bin)

#使用高斯模糊
img = cv.imread('pic/page760x900.jpg', 0)

alpha = 0.05
winSize = 21

img_blur = cv.GaussianBlur(img, (winSize, winSize), 5)
img_bin = np.uint8(img > (1 - alpha) * img_blur) * 255

show(img_bin)