目录
作业要求
实现yen方法:50分 (直接调用库最多25分) 实现聚类法:10分 实现ostu:20分(直接调用库最多10分) 实现人工阈值:10分 结果展示:10分
segment.py
人工阈值法
def thresholding(src, T):
"""
人工阈值法
:param src: 原始图像
:param T: 阈值
:return:分割好的图像
"""
h, w = src.shape
dst = src.copy()
for i in range(h):
for j in range(w):
if src[i, j] < T:
dst[i, j] = 0
else:
dst[i, j] = 255
return dst
聚类法,半自动阈值分割
def clustering(src, T0, T_delta):
"""
聚类法,半自动阈值分割
:param src: 原始图像
:param T0: 初始阈值
:param T_delta: 退出迭代的条件,T_new - T
:return: 分割好的图像 dst
"""
h, w = src.shape
dst = src.copy()
T_new = T0
while 1:
G1, G2 = 0, 0
n1, n2 = 0, 0
T = T_new
# 分割图像,产生两组像素𝑮𝟏(所有像素值<𝑇)和𝑮𝟐(所有像素值>=𝑇)
for i in range(h):
for j in range(w):
if src[i, j] < T:
G1 += src[i, j]
n1 += 1
dst[i, j] = 0
else:
G2 += src[i, j]
n2 += 1
dst[i, j] = 255
# 对 G1和 G2计算平均灰度值 m1和 m2
m1 = G1/n1
m2 = G2/n2
# 计算新阈值
T_new = 0.5 * (m1 + m2)
if abs(T_new - T) < T_delta:
# 退出迭代
break
return dst
OSTU,全自动阈值确定
def ostu(src, L=256):
"""
OSTU全自动阈值确定
:param src: 原始图像
:return: 分割后图像
"""
h, w = src.shape
# 计算直方图
H = np.zeros(L)
for i in range(h):
for j in range(w):
H[src[i][j]] += 1
H /= h*w
# 计算图像平均灰度
avg = 0
for i in range(255):
avg += i*H[i]
var = np.zeros(L)
# 遍历计算每个 T的类间方差
for T in range(L):
# 阈值为𝑇时,G1发生的概率
w1 = 0
for i in range(T):
w1 += H[i]
# 阈值为𝑇时,G2发生的概率
w2 = 1-w1
# 阈值为𝑇时,G1的均值
avg1 = 0
for i in range(T):
avg1 += i * H[i]
# 阈值为𝑇时,G2的均值
avg2 = avg - avg1
# 计算阈值为 T时的方差
var[T] = w1*(avg1-avg)**2 + w2*(avg2-avg)**2
# 获得阈值最大时的 T
T = np.argmax(var)
return thresholding(src, T)
yen分割
chatgpt对《A new criterion for automatic multilevel thresholding》的大致讲解:
该论文由Yen JC于1995年发表在图像处理领域的期刊《IEEE Transactions on Image Processing》上,提出了一种基于最大类间方差的自适应多阈值分割方法,称之为Yen方法。
在这篇论文中,Yen提出了一种新的多阈值分割准则,该准则基于信息熵和累积概率密度函数。具体来说,假设有K个阈值t1, t2, ..., tK-1,将原始图像分割成K个区域。定义pi为第i个区域中像素的概率密度函数,即:
pi = H(ti) - H(ti-1)
其中H(ti)是t为ti时的信息熵,定义为:
H(ti) = -Σpilog(pi)
pi在本文中称为累积概率密度函数。然后,定义Rk为第k个区域的像素灰度值的范围:
Rk = [tk-1, tk), k = 1, 2, ..., K
然后,计算第k个区域的累积概率密度函数Pk(x):
Pk(x) = Σi=1到k(pi * I[x∈Ri])
其中I[x∈Ri]是指x是否在第i个区域中。累积概率密度函数Pk(x)表示x的像素灰度值在k个区域中的分布情况。
然后,定义Pk(x)的熵为:
Gk(x) = -Σi=1到k(pi * log(pi / Pk(x)))
其中pi和Pk(x)同上。Gk(x)可以看作是x的像素灰度值在第k个区域中的信息熵。
最终,多阈值分割的准则定义为:
σ^2 = Σk=1到K-1((Σi=1到k(pi))^2 / (Σi=1到k(pi))^2) * Gk(tK)
其中,σ^2表示多阈值分割的类间方差,Gk(tK)表示最后一个阈值tK的熵,K为总阈值数。
然后使用最大类间方差准则选择最优的阈值集合来分割原始图像,使得类间方差最大。
总的来说,Yen方法使用信息熵和累积概率密度函数来计算多阈值分割的准则,避免了使用传统的直方图方法中需要的对图像进行直方图均衡化或梯度运算的预处理,因此适用于处理低对比度和灰度分布不均匀的图像。
在Yen方法中,我们需要计算每个阈值分割出来的两个类别的类内方差和类间方差。在计算类内方差时,我们需要计算每个类别的像素值的平均值,并计算该类别中所有像素值与该平均值之差的平方和。类间方差是指两个类别之间的方差。我们可以通过计算总体均值与每个类别的均值之间的平方和来计算类间方差。在Yen方法中,我们将类间方差最大的阈值作为最终的分割阈值。
def yen(src):
"""
yen分割
:param src:原始图像
:return:分割好的图像
"""
h, w = src.shape
# 计算直方图
hist, bins = np.histogram(src.ravel(), 256, [0, 256])
# 获取图像的总像素数
total_pixels = h*w
# 计算概率分布:将每个像素值出现的频率除以总像素数
p = hist / total_pixels
# 计算像素概率分布的累积分布函数
w = np.cumsum(p)
# 计算像素值的加权平均值,即每个像素值的期望值
mu = np.cumsum(p * np.arange(0, 256))
mu_t = mu[-1] # 整幅图像的期望值
# 计算每个阈值分割出来的两个类别的类内方差和类间方差
# 计算类间方差
sigma_b_squared = (mu_t * w - mu) ** 2 / (w * (1 - w))
# 处理非有限值,将非有限值的类间方差设置为0
sigma_b_squared[~np.isfinite(sigma_b_squared)] = 0
# 获取最大的类间方差
sigma_b_squared_max = sigma_b_squared.max()
# 找到最大类间方差对应的阈值
T = np.where(sigma_b_squared == sigma_b_squared_max)[0][0]
return thresholding(src, T)
ui_sys.py
使用了PyQt6进行UI设计
使用了GTDesigner工具(可视化制作GUI)和pyUIC工具(将QTdesigner中生成的.ui文件转换为.py文件)
from PyQt6 import QtCore, QtWidgets
from PyQt6.QtGui import QPixmap
from PyQt6.QtWidgets import QFileDialog
import cv2 as cv
from lab04.segment import *
class Ui_Dialog(object):
def __init__(self):
self.org_img = None
# 自动生成,省略
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
#。。。
#。。。
# 设定按钮的功能(自己加上)
self.button_connect()
# 自动生成,省略
def retranslateUi(self, Dialog):
#。。。
#。。。
# 给每个按钮加上点击功能
def button_connect(self):
self.pushButton_3.clicked.connect(self.open_img)
self.pushButton.clicked.connect(lambda: self.show_dst_img(0))
self.pushButton_2.clicked.connect(lambda: self.show_dst_img(1))
self.pushButton_4.clicked.connect(lambda: self.show_dst_img(2))
self.pushButton_5.clicked.connect(lambda: self.show_dst_img(3))
# 打开图片
def open_img(self):
imgName, imgType = QFileDialog.getOpenFileName(None, "打开图片", "img/", "*.png;;*.jpg;;All Files(*)")
org_img = cv.imread(imgName)
self.org_img = cv.cvtColor(org_img, cv.COLOR_RGB2GRAY)
# 显示图片
pix = QPixmap(imgName).scaled(self.label.width(), self.label.height())
self.label.setPixmap(pix)
# 显示转换后的图片
def show_dst_img(self, x):
if x == 0:
# 由于 ui 没有设计可以传递参数的入口,这里使用固定参数
dst = thresholding(self.org_img, 150)
elif x == 1:
# 由于 ui 没有设计可以传递参数的入口,这里使用固定参数
dst = clustering(self.org_img, 100, 15)
elif x == 2:
dst = ostu(self.org_img)
elif x == 3:
dst = yen(self.org_img)
cv.imwrite('dst_img.png', dst)
# 显示图片
pix = QPixmap('dst_img.png').scaled(self.label_2.width(), self.label_2.height())
self.label_2.setPixmap(pix)
start.py
启动程序
if __name__ == '__main__':
# 创建ui,引用 Ui_MainWindow类
app = QApplication(sys.argv)
mainWindow = QMainWindow()
ui = ui_sys.Ui_Dialog()
# 调用 setupUi,创建初始组件
ui.setupUi(mainWindow)
# 创建窗口
mainWindow.show()
# 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)
sys.exit(app.exec())