数字图像处理—9图像阈值分割

9.1        人工阈值分割

  简单阈值分割是通过人为观察确定阈值,将灰度值大于阈值的像素点置为255,而小于或等于该阈值的点设置为0。该方法使用简单,但是准确率不高,使用面窄。使用前我们需了解以下代码:

_, img_b = cv2.threshold(img, 130, 255, cv2.THRESH_BINARY)

130:设置的阈值

255:当像素值超过阈值时设定的最大值

cv2.THRESH_BINARY:二值化方法类型

plt.axis('off')是Matplotlib库中的一个函数调用,用于关闭当前图形的坐标轴显示。

代码如下及运行结果如图7.21

import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
img = cv2.imread('003.bmp', 0)
_, img_b = cv2.threshold(img, 130, 255, cv2.THRESH_BINARY)
plt.subplot(131)
plt.imshow(img, 'gray')
plt.title('原图')
plt.axis('off')
plt.subplot(132)
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
plt.plot(hist)
plt.title('灰度直方图')
plt.subplot(133)
plt.imshow(img_b, 'gray')
plt.title('人工阈值分割图T=130')
plt.axis('off')
plt.show()
图7.21
图7.22

9.2        直方图阈值分割

  直方图阈值分割通常是使用双峰法完成,先要确定第一个峰值,然后定位第二个峰值,最后获得两峰之间的最小值作为阈值。我们需提前了解以下代码:

n, _ = np.histogram(img.ravel(), bins=256, range=[0, 255])

img.ravel():将图像展平为一维数组

np.histogram:统计每个灰度级的像素数量

n:存储各灰度级出现次数的数组

temp = 0

for i in range(256):

    temp1 = np.power(i - f1, 2) * n[i]

    if temp1 > temp:

        temp = temp1

        f2 = i

遍历所有灰度级,计算每个灰度级i的权重值,权重最大的灰度级f2被选为第二峰值

l_mi = np.where(n[f1:f2] == np.min(n[f1:f2]))

np.where:返回谷底的索引位置

代码如下及运行结果如图7.22

import cv2
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
img = cv2.imread('003.bmp', 0)
n, _ = np.histogram(img.ravel(), bins=256, range=[0, 255])
f1 = np.argmax(n)
temp = 0
for i in range(256):
    temp1 = np.power(i - f1, 2) * n[i]
    if temp1 > temp:
        temp = temp1
        f2 = i
if f1 > f2:
    f1, f2 = f2, f1
l_mi = np.where(n[f1:f2] == np.min(n[f1:f2]))
T = f1 + l_mi[0][0]
_, img_b = cv2.threshold(img, T, 255, cv2.THRESH_BINARY)
plt.subplot(131)
plt.imshow(img, 'gray')
plt.title('原图')
plt.axis('off')
plt.subplot(132)
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
plt.plot(hist)
plt.title('灰度直方图')
plt.subplot(133)
plt.imshow(img_b, 'gray')
plt.title('直方图阈值分割图T='+'{:d}'.format(T))
plt.axis('off')
plt.show()

9.3        迭代阈值分割

  迭代阈值分割先将均值作为初始阈值,将图像分成两个子区域,再通过计算两个子区域的均值更新阈值,迭代计算过程直至找到最佳阈值。

代码如下及运行结果如图7.23

import cv2
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
img = cv2.imread('003.bmp', 0)
T = int(np.mean(img))
while True:
    m1 = np.mean(img[img >= T])
    m2 = np.mean(img[img < T])
    if abs((m1 + m2) / 2 - T) < 20:
        break
    else:
        T = int((m1 + m2) / 2)
_, img_b = cv2.threshold(img, T, 255, cv2.THRESH_BINARY)
plt.subplot(121)
plt.imshow(img, 'gray')
plt.title('原图')
plt.axis('off')
plt.subplot(122)
plt.imshow(img_b, 'gray')
plt.title('迭代阈值分割图 T='+'{:d}'.format(T))
plt.axis('off')
plt.show()
图7.23
图7.24

9.4        最大类间方差阈值分割

  最大类间方差的核心是通过公式计算类间方差,并使之最大化。这里使用两种方法实现:一种是按照公式自己编写代码实现;另一种是利用OpenCV自带的函数实现。我们需了解以下代码:

tem = w1 * w2 * np.power((mean1 - mean2), 2)为计算类间方差的公式

T1, img_b1 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

T1:计算得到的最优阈值

cv2.THRESH_BINARY + cv2.THRESH_OTSU:组合标志位,表示使用Otsu算法和二值化模式

代码如下及运行结果如图7.24

import cv2
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
img = cv2.imread('003.bmp', 0)
t = 0
T = 0
for i in range(256):
    mean1 = np.mean(img[img < i])
    mean2 = np.mean(img[img >= i])
    w1 = np.sum(img < i) / np.size(img)
    w2 = np.sum(img >= i) / np.size(img)
    tem = w1 * w2 * np.power((mean1 - mean2), 2)
    if tem > t:
        T = i
        t = tem
_, img_b = cv2.threshold(img, T, 255, cv2.THRESH_BINARY)
T1, img_b1 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
plt.subplot(131)
plt.imshow(img, 'gray')
plt.title('原图')
plt.axis('off')
plt.subplot(132)
plt.imshow(img_b, 'gray')
plt.title('最大类间方差阈值分割图 T='+'{:d}'.format(T))
plt.axis('off')
plt.subplot(133)
plt.imshow(img_b1, 'gray')
plt.title('最大类间方差阈值分割图T='+'{:d}'.format(int(T1)))
plt.axis('off')
plt.show()

9.5        拓展

  通过本次实验的学习,我们可以自己制作一个可以用进度条控制阀值的图像分割界面,并加入到之前所做的图像处理界面中。所需补充代码如下及运行结果如图一

# 创建阈值分割进度条和标签
threshold_layout = QHBoxLayout()
self.threshold_slider = QSlider(Qt.Horizontal)
self.threshold_slider.setRange(0, 255)
self.threshold_slider.setValue(128)
self.threshold_slider.valueChanged.connect(self.threshold_segmentation)
self.threshold_label = QLabel(f"阈值: {self.threshold_slider.value()}")
threshold_layout.addWidget(self.threshold_slider)
threshold_layout.addWidget(self.threshold_label)
main_layout.addLayout(threshold_layout)


def threshold_segmentation(self):
    if 'original' not in self.image_data:
        return
    img = self.image_data['original']
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    threshold = self.threshold_slider.value()
    self.threshold_label.setText(f"阈值: {threshold}")
    _, result = cv2.threshold(gray_img, threshold, 255, cv2.THRESH_BINARY)
    result = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR)
    self.image_data['processed'] = result
    self.show_image(result, self.processed_label)
图1

9.6        总结

通过本次实验的学习,我掌握了图像阈值分割的原理,同时完成了直方图双峰法、迭代值分割、最大类间方差阀值分割的设计,并且成功将该功能补充到了先前设计的图像处理界面中,使我对课程的理解和编码能力都得到了提升。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值