遗传算法图像拟合

图像拟合

1 遗传算法简介

遗传算法是一种迭代优化算法,每次对**个体(individuals)组成的种群(Population)**进行优化,其中每个个体对应一个问题的可行解。

1.1 适应度函数

每个个体都由适应度函数(Fitness function)来进行评价,一般而言,适应度越好的个体能更好地解决问题。

算法根据个体的适应度(fitness)进行选择,将一些个体指定为双亲,由双亲产生子代个体(offspring)形成新的一代。

  1. 新一代和上一代的大小形式应该相同,但是我们只保留新一代而删除上一代,即迭代。
  2. 适应度越好的个体产生子代的概率越大。
  3. 子代的属性是由其双亲组合而来的,具体组成方式多种多样。

若适应度函数设计良好,种群就会更快更准的收敛于最优解。

1.2 简单遗传算法(SGA)

以下是SGA的算法表示:

BEGIN
	Generate initial population;生成初代种群
	Compute fitness of each individual; 计算每个个体(染色体)的适应度
	REPEAT 重度以下操作来生成新的种群
		Selection 选择要成为双亲的个体,鼓励进化,降低多样性,一般选取适应度较高的个体进行
		Crossover 双亲交叉,生成子代(这个类似于繁殖)
		Mutation 变异,子代的某些属性进行了一些变化,有一定的变异率(孩子不可能和父母一模一样)
		Evaluate new popilation 评估新的种群
	UNTIL population has converge
END

1.3 主要问题

  1. 如何表示个体?(需要进行编码和解码)
  2. 如何初始化种群?(随机化方法)
  3. 如何进行选择?(设置适应度函数)
  4. 如何进行交叉(繁殖)?
  5. 什么时候停止算法?

1.4 编码和解码

定义

将问题结构变换为位串形式编码表示的过程叫编码(Encoding);相反将位串形式编码表示变换为原问题结构的过程叫解码(Decoding)。

通常将解对应的参数(基因)连接成位串以构成染色体(个体)。

本质上所有的字符都可以用编码染色体,但是通常我们采用二进制来进行编码。

编码方式

二进制编码,实数编码,排列编码,规则列表等

基因的排列顺序通常很重要,好的编码方式对GA的性能有非常重要的影响。

二进制编码
  1. 离散型表示
    对于二类问题可以直接使用二进制,在一个位串中的个体正例记为1,个体反例记为0;对于多类问题可以用整数记录,方法同上,但是会潜在给类别定义顺序。
  2. 实数表示
    可以用二进制转化为十进制来表示实数
  3. 浮点数表示
    可以用二进制来表示某一区间内的浮点数。
二进制解码

解码过程即编码的逆过程,采用逆向思维解决便可。

其他编码方式简述
  1. 实数编码:一组实数,可以将向量映射为向量中的一个元素。
  2. 基于序列的表示:

1.5 个体评估

如上文所述,每个个体都对应一个适应度,例如在TSP问题中的适应度即为路途的代价。选择一个正确的适应度函数十分重要,但是也非常困难。通常我们是要最大化或者最小化适应度函数。

1.6 选择策略

选择操作也是GA中非常重要的一环,但是也是非常耗时的一环。选择主要是为了保证较优的个体有更高的繁殖机会,是种群进化的主要驱动力。但是要注意,给予次优个体一定概率的繁殖机会能增加种群的多样性,有利于产生有用的基因。

常用的选择策略有:轮盘赌法;锦标赛法。

轮赌法

选择任意一个个体i的概率为
f i / ∑ f j f_i/\sum{f_j} fi/fj
其中fi为i的适应度,由此可以看出,更好的个体,即适应度较高的个体会有更大的空间,也就是更多机会被选中。

具体算法:

  1. 计算所有染色体的适应度之和T以及运行和(即当前个体与之前所有个体的适应度之和)
  2. 生成0-T之间的随机数N
  3. 返回运行和大于等于N的第一个染色体
    因此,选择概率于适应度成正比

缺点:

  1. 优势个体占据整个种群的速度非常快,容易导致早熟,陷入局部最优
  2. 适应度值非常接近时,选择压力变低
锦标赛法

一般而言,锦标赛指从种群中随机抽取k个个体进行比较,选取k个个体中最好的一个,k为锦标赛的规模

二值锦标赛:随机选择两个个体,适应度高的个体被选中为双亲染色体

概率二值锦标赛:随机选择两个个体,以概率p来选择适应度高的

大规模锦标赛:随机选择n个个体,适应度最高的个体被选中

1.7 繁殖

繁殖包括交叉和变异

交叉

两个双亲染色体产生两个子代个体,有一定的概率两个双亲染色体直接成为子代,也有一定概率双亲染色体随机组合生成子代

包括:单点交叉,两点交叉,均匀交叉,算术交叉

交叉操作与编码方式相关,简单的交叉可能导致非法子代,例如TSP中交叉产生的子代路径可能完全不通,通常可以使用均匀交叉来回避这个问题,。实数编码也可以使用均匀交叉或者算术交叉。

变异

子代个体以一定的概率随机改变自己的基因,一般而言变异率会很低。变异也是生成子代的过程,主导个体的多样性。注意,交叉之恶能对现有基因吃进行重组,而变异可以生成新的基因。变异程度是很重要的,应该慎重设置,我们是希望生成有效的染色体。

对二进制编码的变异,可以随机选取一个值进行改变。

对实数编码的变异,通过添加随机噪声,如高斯噪声。
x ’ i = x i + N ( 0 , σ ) x’_i=x_i+N(0,\sigma) xi=xi+N(0,σ)
交换变异:任意选取位序中的两个值进行交换。

2 实验内容

### 2.1 实验要求

本次实验的目的是完成图像拟合代码的调试,并撰写实验运行报告。报告应为Word文档,包含以下内容:

  1. 染色体编码方式:说明所使用的染色体编码方式,以及编码方式的具体实现。
  2. 选择策略
  3. 交叉策略
  4. 变异策略
  5. 实验结果

2.2 实验内容

本实验旨在通过遗传算法实现图像拟合,具体内容如下:

  1. 阅读示例代码并理解其实现思路,确定项目的编程形式。

  2. 确定使用的染色体编码方式,每个染色体由多个三角形构成,第i个三角形的特征向量
    x i = ( x i 1 , x i 2 , x i 3 , x i 4 , x i 5 , x i 6 , x i 7 , x i 8 , x i 9 , x i 10 ) x_i = (x_{i1},x_{i2},x_{i3},x_{i4},x_{i5},x_{i6},x_{i7},x_{i8},x_{i9},x_{i10}) xi=(xi1,xi2,xi3,xi4,xi5,xi6,xi7,xi8,xi9,xi10)
    前六个元素为三角形三个角的坐标,即(ax,ay),(bx,by),(cx,cy),

    后四个元素为三角形的演示与透明度,即(r, g, b, alpha).

  3. 确定选择策略,个体适应度为当前个体像素差的平方,值越小适应度越高。

  4. 确定交叉策略,将两个种中的某些三角形进行交换。

  5. 确定变异策略,个体中部分三角形进行变异。

  6. 运行遗传算法程序,并记录初代、中间代和最终结果的最差与最优个体图像,并标明对应的进化代数。

    绘制最差、最优和平均适应度值随进化代数变化的收敛曲线图,其中横轴为进化代数,纵轴为适应度值。图中应有3条曲线分别对应每一代最差、最优和平均适应度值的变化。

  7. 对实验结果进行分析和讨论,评价所使用的遗传算法在图像拟合方面的性能和优缺点,并提出改进方案。

2.3 编程示例

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PIL import Image, ImageDraw
import os
import gc
import random as r
import minpy.numpy as np

class Color(object):
    '''
    定义颜色的类,这个类包含r,g,b,a表示颜色属性
    '''
    def __init__(self):
        self.r = r.randint(0, 255)
        self.g = r.randint(0, 255)
        self.b = r.randint(0, 255)
        self.a = r.randint(95, 115)


def mutate_or_not(rate):
    '''
    生成随机数,判断是否需要变异
    '''
    return True if rate > r.random() else False


class Triangle(object):
    '''
    定义三角形的类
    属性:
            ax,ay,bx,by,cx,cy:表示每个三角形三个顶点的坐标
            color 			 : 表示三角形的颜色
            img_t			 : 三角形绘制成的图,用于合成图片
    方法:
            mutate_from(self, parent):      从父代三角形变异
            draw_it(self, size=(256, 256)): 绘制三角形
    '''


    max_mutate_rate = 0.08
    mid_mutate_rate = 0.3
    min_mutate_rate = 0.8


    def __init__(self, size=(255, 255)):
        self.ax = r.randint(0, size[0])
        self.ay = r.randint(0, size[1])
        self.bx = r.randint(0, size[0])
        self.by = r.randint(0, size[1])
        self.cx = r.randint(0, size[0])
        self.cy = r.randint(0, size[1])
        self.color = Color()
        self.img_t = None


    def mutate_from(self, parent):
        if mutate_or_not(self.max_mutate_rate):
            self.ax = r.randint(0, 255)
            self.ay = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.ax = min(max(0, parent.ax + r.randint(-15, 15)), 255)
            self.ay = min(max(0, parent.ay + r.randint(-15, 15)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.ax = min(max(0, parent.ax + r.randint(-3, 3)), 255)
            self.ay = min(max(0, parent.ay + r.randint(-3, 3)), 255)

        if mutate_or_not(self.max_mutate_rate):
            self.bx = r.randint(0, 255)
            self.by = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.bx = min(max(0, parent.bx + r.randint(-15, 15)), 255)
            self.by = min(max(0, parent.by + r.randint(-15, 15)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.bx = min(max(0, parent.bx + r.randint(-3, 3)), 255)
            self.by = min(max(0, parent.by + r.randint(-3, 3)), 255)

        if mutate_or_not(self.max_mutate_rate):
            self.cx = r.randint(0, 255)
            self.cy = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.cx = min(max(0, parent.cx + r.randint(-15, 15)), 255)
            self.cy = min(max(0, parent.cy + r.randint(-15, 15)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.cx = min(max(0, parent.cx + r.randint(-3, 3)), 255)
            self.cy = min(max(0, parent.cy + r.randint(-3, 3)), 255)
        # color
        if mutate_or_not(self.max_mutate_rate):
            self.color.r = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.color.r = min(max(0, parent.color.r + r.randint(-30, 30)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.color.r = min(max(0, parent.color.r + r.randint(-10, 10)), 255)

        if mutate_or_not(self.max_mutate_rate):
            self.color.g = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.color.g = min(max(0, parent.color.g + r.randint(-30, 30)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.color.g = min(max(0, parent.color.g + r.randint(-10, 10)), 255)

        if mutate_or_not(self.max_mutate_rate):
            self.color.b = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.color.b = min(max(0, parent.color.b + r.randint(-30, 30)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.color.b = min(max(0, parent.color.b + r.randint(-10, 10)), 255)
        # alpha
        if mutate_or_not(self.mid_mutate_rate):
            self.color.a = r.randint(95, 115)
        # if mutate_or_not(self.mid_mutate_rate):
        #     self.color.a = min(max(0, parent.color.a + r.randint(-30, 30)), 255)
        # if mutate_or_not(self.min_mutate_rate):
        #     self.color.a = min(max(0, parent.color.a + r.randint(-10, 10)), 255)


    def draw_it(self, size=(256, 256)):
        self.img_t = Image.new('RGBA', size)
        draw = ImageDraw.Draw(self.img_t)
        draw.polygon([(self.ax, self.ay),
                      (self.bx, self.by),
                      (self.cx, self.cy)],
                     fill=(self.color.r, self.color.g, self.color.b, self.color.a))
        return self.img_t


class Canvas(object):
    '''
    定义每一张图片的类
    属性:
            mutate_rate	 : 变异概率
            size		 : 图片大小
            target_pixels: 目标图片像素值
    方法:
            add_triangles(self, num=1)      : 在图片类中生成num个三角形
            mutate_from_parent(self, parent): 从父代图片对象进行变异
            calc_match_rate(self)			: 计算环境适应度
            draw_it(self, i)				: 保存图片
    '''


    mutate_rate = 0.01
    size = (256, 256)
    target_pixels = []


    def __init__(self):
        self.triangles = []
        self.match_rate = 0
        self.img = None


    def add_triangles(self, num=1):
        for i in range(0, num):
            triangle = Triangle()
            self.triangles.append(triangle)


    def mutate_from_parent(self, parent):
        flag = False
        for triangle in parent.triangles:
            t = triangle
            if mutate_or_not(self.mutate_rate):
                flag = True
                a = Triangle()
                a.mutate_from(t)
                self.triangles.append(a)
                continue
            self.triangles.append(t)
        if not flag:
            self.triangles.pop()
            t = parent.triangles[r.randint(0, len(parent.triangles) - 1)]
            a = Triangle()
            a.mutate_from(t)
            self.triangles.append(a)


    def calc_match_rate(self):
        if self.match_rate > 0:
            return self.match_rate
        self.match_rate = 0
        self.img = Image.new('RGBA', self.size)
        draw = ImageDraw.Draw(self.img)
        draw.polygon([(0, 0), (0, 255), (255, 255), (255, 0)], fill=(255, 255, 255, 255))
        for triangle in self.triangles:
            self.img = Image.alpha_composite(self.img, triangle.img_t or triangle.draw_it(self.size))    
        # 与下方代码功能相同,此版本便于理解但效率低
        # pixels = [self.img.getpixel((x, y)) for x in range(0, self.size[0], 2) for y in range(0, self.size[1], 2)]
        # for i in range(0, min(len(pixels), len(self.target_pixels))):
        #     delta_red   = pixels[i][0] - self.target_pixels[i][0]
        #     delta_green = pixels[i][1] - self.target_pixels[i][1]
        #     delta_blue  = pixels[i][2] - self.target_pixels[i][2]
        #     self.match_rate += delta_red   * delta_red   + \
        #                        delta_green * delta_green + \
        #                        delta_blue  * delta_blue
        arrs = [np.array(x) for x in list(self.img.split())]    # 分解为RGBA四通道
        for i in range(3):                                      # 对RGB通道三个矩阵分别与目标图片相应通道作差取平方加和评估相似度
            self.match_rate += np.sum(np.square(arrs[i]-self.target_pixels[i]))[0]

    def draw_it(self, i):
        #self.img.save(os.path.join(PATH, "%s_%d_%d_%d.png" % (PREFIX, len(self.triangles), i, self.match_rate)))
        self.img.save(os.path.join(PATH, "%d.png" % (i)))


def main():
        global LOOP, PREFIX, PATH, TARGET, TRIANGLE_NUM
        # 声明全局变量
        img = Image.open(TARGET).resize((256, 256)).convert('RGBA')
        size = (256, 256)
        Canvas.target_pixels = [np.array(x) for x in list(img.split())]
        # 生成一系列的图片作为父本,选择其中最好的一个进行遗传
        parentList = []
        for i in range(20):
            print('正在生成第%d个初代个体' % (i))
            parentList.append(Canvas())
            parentList[i].add_triangles(TRIANGLE_NUM)
            parentList[i].calc_match_rate()
        parent = sorted(parentList, key=lambda x: x.match_rate)[0]
        del parentList
        gc.collect()
        # 进入遗传算法的循环
        i = 0
        while i < 30000:
            childList = []
            # 每一代从父代中变异出10个个体
            for j in range(10):
                childList.append(Canvas())
                childList[j].mutate_from_parent(parent)
                childList[j].calc_match_rate()
            child = sorted(childList, key=lambda x: x.match_rate)[0]
            # 选择其中适应度最好的一个个体
            del childList
            gc.collect()
            parent.calc_match_rate()
            if i % LOOP == 0:
                print ('%10d parent rate %11d \t child1 rate %11d' % (i, parent.match_rate, child.match_rate))
            parent = parent if parent.match_rate < child.match_rate else child
            # 如果子代比父代更适应环境,那么子代成为新的父代
            # 否则保持原样
            child = None
            if i % LOOP == 0:
                # 每隔LOOP代保存一次图片
                parent.draw_it(i)
                #print(parent.match_rate)
                #print ('%10d parent rate %11d \t child1 rate %11d' % (i, parent.match_rate, child.match_rate))
            i += 1


'''
定义全局变量,获取待处理的图片名
'''
NAME = input('请输入原图片文件名:')
LOOP = 100
PREFIX = NAME.split('/')[-1].split('.')[0]  # 取文件名
PATH = os.path.abspath('.')  # 取当前路径
PATH = os.path.join(PATH,'results')
TARGET = NAME  # 源图片文件名
TRIANGLE_NUM = 256  # 三角形个数

if __name__ == '__main__':
    #print('开始进行遗传算法')
    main()

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Rvosuke

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

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

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

打赏作者

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

抵扣说明:

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

余额充值