本实验采用遗传算法和大津算法确定图像二值化的最佳阈值,从而对图像进行二值化分割
最大类间方差法是1979年由日本学者大津提出的,是一种自适应阈值确定的方法,又叫大津法,简称OTSU,是一种基于全局的二值化算法.
它是根据图像的灰度特性,将图像分为前景和背景两个部分。当取最佳阈值时,两部分之间的差别应该是最大的,在OTSU算法中所采用的衡量差别的标准就是较为常见的最大类间方差。前景和背景之间的类间方差如果越大,就说明构成图像的两个部分之间的差别越大,当部分目标被错分为背景或部分背景被错分为目标,都会导致两部分差别变小,当所取阈值的分割使类间方差最大时就意味着错分概率最小
1.计算待分割图像的图像灰度直方图
2.初始化种群,随机产生M个个体,并对图像的灰度值进行编码
3.根据OTSU算法计算每个个体的适应度值
4.建立下一代种群,进行一定数量的遗传操作, 包括顺次执行的选择操作、 交叉操作和变异操作
5.选择操作(自然选择:将当代种群中的个体按照适应度值由大到小选择前M个个体,将他们复制到下一代种群中。同时使用随机的方式,保留一定比例落后的个体,让其存活,将他们复制到下一代种群中
6.交叉操作(繁殖),随机选出父体和母体,进行交叉操作,类比染色体的交叉,随机选择交叉点。交叉完成的新个体补充到下一代种群中,直到种群数量足够为止
7.变异操作(基因变异:将上述交叉操作产生的种群中的个体的变异率P进行变化,如某一个体的某一个基因进行突变
8.进行多次迭代,直到种群进化了一定数量的代数以后,选择其最优先的个体,优秀个体转换成分割阈值,处理图像,显示效果
1.如何建立灰度图和染色体的联系:
灰度图中,每个像素点的灰度值从0到255,二进制形式是8比特位,将八个比特位看成一个染色体,每一个比特位就是一个基因点。
2.如何考虑选择下一代种群:
首先先把种群中的每一个个体计算适应度,借助OTSU算法,计算得出个体适应度,适应度的大小即可表示个体的优劣程度。但不能只选出色的个体,有些不好的个体可能会产生优质的下一代个体,因此在选择的时候,考虑首先选择前百分比a的优秀个体,剩下的个体中,随机选取比例b的个体幸存,其余淘汰。
3.如何考虑变异:
考虑到真实的环境中变异的可能性比较小,所以设定了一个变异概率来控制变异的可能性。
1.展示二值化后的图
2.输出阈值
class GA: # 定义遗传算法类
def __init__(self, image, M): # 构造函数,进行初始化以及编码
self.image = image
self.M = M # 初始化种群的个体数
self.length = 8 # 每条染色体基因长度为8(0-255)
self.species = np.random.randint(0, 256, self.M) # 给种群随机编码
self.select_rate = 0.5 # 选择的概率(用于适应性没那么强的染色体),小于该概率则被选,否则被丢弃
self.strong_rate = 0.3 # 选择适应性强染色体的比率
self.bianyi_rate = 0.05 # 变异的概率
def Adaptation(self, ranseti): # 进行染色体适应度的评估
fit = OTSU().otsu(self.image, ranseti)
return fit
def selection(self): # 进行个体的选择,前百分之30的个体直接留下,后百分之70按概率选择
fitness = []
for ranseti in self.species: # 循环遍历种群的每一条染色体,计算保存该条染色体的适应度
fitness.append((self.Adaptation(ranseti), ranseti))
fitness1 = sorted(fitness, reverse=True) # 逆序排序,适应度高的染色体排前面
for i, j in zip(fitness1, range(self.M)):
fitness[j] = i[1]
parents = fitness[:int(len(fitness) *
self.strong_rate)] # 适应性特别强的直接留下来
for ranseti in fitness[int(len(fitness) *
self.strong_rate):]: # 挑选适应性没那么强的染色体
if np.random.random() < self.select_rate: # 随机比率,小于则留下
parents.append(ranseti)
return parents
def crossover(self, parents): # 进行个体的交叉
children = []
child_count = len(self.species) - len(parents) # 需要产生新个体的数量
while len(children) < child_count:
fu = np.random.randint(0, len(parents)) # 随机选择父亲
mu = np.random.randint(0, len(parents)) # 随机选择母亲
if fu != mu:
position = np.random.randint(0,
self.length) # 随机选取交叉的基因位置(从右向左)
mask = 0
for i in range(position): # 位运算
mask = mask | (1 << i) # mask的二进制串最终为position个1
fu = parents[fu]
mu = parents[mu]
child = (fu & mask) | (
mu & ~mask) # 孩子获得父亲在交叉点右边的基因、母亲在交叉点左边(包括交叉点)的基因,不是得到两个新孩子
children.append(child)
self.species = parents + children # 产生的新的种群
def bianyi(self): # 进行个体的变异
for i in range(len(self.species)):
if np.random.random() < self.bianyi_rate: # 小于该概率则进行变异,否则保持原状
j = np.random.randint(0, self.length) # 随机选取变异基因位置点
self.species[i] = self.species[i] ^ (1 << j) # 在j位置取反
def evolution(self): # 进行个体的进化
parents = self.selection()
self.crossover(parents)
self.bianyi()
def yuzhi(self): # 返回适应度最高的这条染色体,为最佳阈值
fitness = []
for ranseti in self.species: # 循环遍历种群的每一条染色体,计算保存该条染色体的适应度
fitness.append((self.Adaptation(ranseti), ranseti))
fitness1 = sorted(fitness, reverse=True) # 逆序排序,适应度高的染色体排前面
for i, j in zip(fitness1, range(self.M)):
fitness[j] = i[1]
return fitness[0]
用来算个体适应度
class OTSU: # 定义大津算法类
def otsu(self, image, yuzhi): # 计算该条染色体(个体)的适应度
image = np.transpose(np.asarray(image)) # 转置
size = image.shape[0] * image.shape[1]
bin_image = image < yuzhi
summ = np.sum(image)
w0 = np.sum(bin_image)
sum0 = np.sum(bin_image * image)
w1 = size - w0
if w1 == 0:
return 0
sum1 = summ - sum0
mean0 = sum0 / (w0 * 1.0)
mean1 = sum1 / (w1 * 1.0)
fitt = w0 / (size * 1.0) * w1 / (size * 1.0) * (
mean0 - mean1) * (mean0 - mean1)
return fitt
import numpy as np
from PIL import Image
class GA: # 定义遗传算法类
def __init__(self, image, M): # 构造函数,进行初始化以及编码
self.image = image
self.M = M # 初始化种群的个体数
self.length = 8 # 每条染色体基因长度为8(0-255)
self.species = np.random.randint(0, 256, self.M) # 给种群随机编码
self.select_rate = 0.5 # 选择的概率(用于适应性没那么强的染色体),小于该概率则被选,否则被丢弃
self.strong_rate = 0.3 # 选择适应性强染色体的比率
self.bianyi_rate = 0.05 # 变异的概率
def Adaptation(self, ranseti): # 进行染色体适应度的评估
fit = OTSU().otsu(self.image, ranseti)
return fit
def selection(self): # 进行个体的选择,前百分之30的个体直接留下,后百分之70按概率选择
fitness = []
for ranseti in self.species: # 循环遍历种群的每一条染色体,计算保存该条染色体的适应度
fitness.append((self.Adaptation(ranseti), ranseti))
fitness1 = sorted(fitness, reverse=True) # 逆序排序,适应度高的染色体排前面
for i, j in zip(fitness1, range(self.M)):
fitness[j] = i[1]
parents = fitness[:int(len(fitness) *
self.strong_rate)] # 适应性特别强的直接留下来
for ranseti in fitness[int(len(fitness) *
self.strong_rate):]: # 挑选适应性没那么强的染色体
if np.random.random() < self.select_rate: # 随机比率,小于则留下
parents.append(ranseti)
return parents
def crossover(self, parents): # 进行个体的交叉
children = []
child_count = len(self.species) - len(parents) # 需要产生新个体的数量
while len(children) < child_count:
fu = np.random.randint(0, len(parents)) # 随机选择父亲
mu = np.random.randint(0, len(parents)) # 随机选择母亲
if fu != mu:
position = np.random.randint(0,
self.length) # 随机选取交叉的基因位置(从右向左)
mask = 0
for i in range(position): # 位运算
mask = mask | (1 << i) # mask的二进制串最终为position个1
fu = parents[fu]
mu = parents[mu]
child = (fu & mask) | (
mu & ~mask) # 孩子获得父亲在交叉点右边的基因、母亲在交叉点左边(包括交叉点)的基因,不是得到两个新孩子
children.append(child)
self.species = parents + children # 产生的新的种群
def bianyi(self): # 进行个体的变异
for i in range(len(self.species)):
if np.random.random() < self.bianyi_rate: # 小于该概率则进行变异,否则保持原状
j = np.random.randint(0, self.length) # 随机选取变异基因位置点
self.species[i] = self.species[i] ^ (1 << j) # 在j位置取反
def evolution(self): # 进行个体的进化
parents = self.selection()
self.crossover(parents)
self.bianyi()
def yuzhi(self): # 返回适应度最高的这条染色体,为最佳阈值
fitness = []
for ranseti in self.species: # 循环遍历种群的每一条染色体,计算保存该条染色体的适应度
fitness.append((self.Adaptation(ranseti), ranseti))
fitness1 = sorted(fitness, reverse=True) # 逆序排序,适应度高的染色体排前面
for i, j in zip(fitness1, range(self.M)):
fitness[j] = i[1]
return fitness[0]
class OTSU: # 定义大津算法类
def otsu(self, image, yuzhi): # 计算该条染色体(个体)的适应度
image = np.transpose(np.asarray(image)) # 转置
size = image.shape[0] * image.shape[1]
bin_image = image < yuzhi
summ = np.sum(image)
w0 = np.sum(bin_image)
sum0 = np.sum(bin_image * image)
w1 = size - w0
if w1 == 0:
return 0
sum1 = summ - sum0
mean0 = sum0 / (w0 * 1.0)
mean1 = sum1 / (w1 * 1.0)
fitt = w0 / (size * 1.0) * w1 / (size * 1.0) * (
mean0 - mean1) * (mean0 - mean1)
return fitt
def transition(yu, image): # 确定好最佳阈值后,将原来的图转变成二值化图
temp = np.asarray(image)
print("灰度值矩阵为:")
print(temp) # 展现灰度值矩阵
array = list(np.where(temp < yu, 0,
255).reshape(-1)) # 满足temp