实验-基于遗传算法的图像二值化(python)

实验内容

本实验采用遗传算法和大津算法确定图像二值化的最佳阈值,从而对图像进行二值化分割

实验原理

大津算法(OTSU)

最大类间方差法是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<yu,输出0,否则输出255
    image.putdata(array)
    image.show()
    image.save('D:/2.jpg')


def main():
    tu = Image.open(file)
    tu.show()  # 先展现出原图
    gray = tu.convert('L')  # 转换为灰度图像,每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度。
    ga = GA(gray, 16)
    print("种群变化为:")
    for x in range(100):  # 假设迭代次数为100
        ga.evolution()
        print(ga.species)
    max_yuzhi = ga.yuzhi()
    print("最佳阈值为:", max_yuzhi)
    transition(max_yuzhi, gray)


file = 'D:/1.jpg'
main()

实验结果

原图:
在这里插入图片描述
二值化后:
在这里插入图片描述
程序输出:
可以看到种群的变化,以及最佳阈值情况
在这里插入图片描述
在这里插入图片描述

  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yarhanry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值