python实现使用遗传算法进行图片拟合
作者:艾醒 发布时间:2022-05-25 03:31:53
引言
算法思路
假设我们有这样一个生物族群,他们的每个基因片段都是一个个三角形(即只含三个点和颜色信息),他们每个个体表现出的性状就是若干个三角形叠加在一起。假设我们有一张图片可以作为这种生物族群最适应环境的性状,即长得越像这张图片的越能适应环境,越不像这张图片的越容易被淘汰。
当然作为一个正常的生物族群,他应该也会有正常的繁衍以产生新个体。产生新个体的过程中来自父亲和母亲的基因会进行交叉互换和变异,变异又可以有增加基因片段、减少基因片段以及不同位置的基因片段顺序互换。
但是一个族群不能够无限的繁殖,我们假设环境资源只能容纳有限的生物族群规模,所以我们在产生足够多的后代之后就要把他们放到族群里与族群内部进行竞争淘汰。这样一来,通过不断地淘汰,这个族群就会越来越像我们给定的图片。这就是算法的基本思路。
流程图
下面我们来看一下实现过程,为了方便解释,我们会结合python建立类似于伪代码的函数进行解释,不能直接运行,具体可运行的代码可直接下载参照最后的类封装的完整代码
预备知识及准备工作
打开图片
我们使用imageio中的imread打开图片,这里为了方便后续使用我们建立OpenImg函数,返回打开的图片img(格式为array),图片的格式(方便后期图片的保存),图片的大小:row和col(为后期画图做准备)。
from imageio import imread
def OpenImg(imgPath):
img = imread(imgPath)
row, col = img.shape[0], img.shape[1]
return img, imgPath.split(".")[-1], row, col
随机生成生物族群
我们假设一个族群的最大生物个数为max_group,用groups来表示族群,g为族群内的生物个体,运用随机数随机产生个体。
from random import choice
for i in range(max_group):
g = []
for j in range(features):
tmp = [[choice(np.linspace(0, row, features)), choice(np.linspace(0, col, features))] for x in range(3)]
tmp.append("#" + ''.join(choice('0123456789ABCDEF') for x in range(6)))
g.append(tmp.copy())
groups.append(g.copy())
按照生物性状画图
我们使用PIL画图,首先我们建立一个空白的画布,然后再将个体的特征(三角形)画到图上。
from PIL import Image, ImageDraw
def to_image(g):
array = np.ndarray((img.shape[0], img.shape[1], img.shape[2]), np.uint8)
array[:, :, 0] = 255
array[:, :, 1] = 255
array[:, :, 2] = 255
newIm1 = Image.fromarray(array)
draw = ImageDraw.Draw(newIm1)
for d in g:
draw.polygon((d[0][0], d[0][1], d[1][0], d[1][1], d[2][0], d[2][1]), d[3])
return newIm1
对比生物个体和目标图片的相似度
使用structural_similarity对比两个图片的相似度,此时两个图片都应该是array类型
from skimage.metrics import structural_similarity
def getSimilar(g) -> float:
newIm1 = to_image(g)
ssim = structural_similarity(np.array(img), np.array(newIm1), multichannel=True)
return ssim
保存图片
调用我们之前建立好的to_image函数先将个体转换成图片,然后将图片保存即可。
def draw_image(g, cur, imgtype):
image1 = to_image(g)
image1.save(os.path.join(str(cur) + "." + imgtype))
算法主体
交叉互换
考虑到后期的基因片段的增添和减少,所以应该分为两步,其一是相重合的位置进行交叉互换,以及对于多出来的尾部进行交叉互换,我们按照随机率random_rate和重合位置长度min_locate来确定交换的位数。然后我们使用sample选出若干位置进行互换。交换结束后再对尾部进行互换
random_rate = random()
def exchange(father, mother)->[]:
# 交换
# 随机生成互换个数
min_locate = min(len(father), len(mother))
n = randint(0, int(random_rate * min_locate))
# 随机选出多个位置
selected = sample(range(0, min_locate), n)
# 交换内部
for s in selected:
father[s], mother[s] = mother[s], father[s]
# 交换尾部
locat = randint(0, min_locate)
fhead = father[:locat].copy()
mhead = mother[:locat].copy()
ftail = father[locat:].copy()
mtail = mother[locat:].copy()
# print(fhead, ftail, mhead, mtail)
fhead.extend(mtail)
father = fhead
mhead.extend(ftail)
mother = mhead
return [father, mother]
基因突变
随机选择的原理和目的与上面类似,这里我们把重新生成某个基因片段的信息看作基因突变。
random_rate = random()
def mutation(gen):
# 突变
# 随机生成变异个数
n = int(randint(1, 100) / 1000 * len(gen))
selected = sample(range(0, len(gen)), n)
for s in selected:
tmp = [[choice(np.linspace(0, row, 100)), choice(np.linspace(0, col, 100))] for x inrange(3)]
tmp.append("#" + ''.join(choice('0123456789ABCDEF') for x in range(6)))
gen[s] = tmp
return gen
基因片段易位
在个体的基因组内随机选择基因片段进行位置互换。
def move(gen):
# 易位
exchage = int(randint(1, 100) / 1000 * len(gen))
for e in range(exchage):
sec1 = randint(0, len(gen) - 1)
sec2 = randint(0, len(gen) - 1)
gen[sec1], gen[sec2] = gen[sec2], gen[sec1]
return gen
增加基因片段
直接在个体基因组后面添加随机产生的基因片段即可。
features = 100
def add(gen):
# 增加
n = int(randint(1, 100) / 1000 * len(gen))
for s in range(n):
tmp = [[choice(np.linspace(0, row,features)),choice(np.linspace(0, col, features))] for x in range(3)]
tmp.append("#" + ''.join(choice('0123456789ABCDEF') for x in range(6)))
gen.append(tmp)
return gen
减少基因片段
随机减少个体的若干基因片段
def cut(self, gen):
# 减少
n = int(randint(1, 100) / 1000 * len(gen))
selected = sample(range(0, len(gen)), n)
g = []
for gp in range(len(gen)):
if gp not in selected:
g.append(gen[gp])
return g
变异
以此调用以上的突变、易位、增添和减少产生4种状态的基因片段
def variation(gen):
# 变异
gen = mutation(gen.copy())
gen1 = move(gen.copy())
gen2 = add(gen1.copy())
gen3 = cut(gen2.copy())
return [gen, gen1, gen2, gen3]
繁殖
繁殖过程包括交叉互换和变异,直接调用之前构造的函数即可
def breeds(father, mother):
new1, new2 = exchange(father.copy(), mother.copy())
# 变异
new3, new4, new5, new6 = variation(father.copy())
new7, new8, new9, new10 = variation(mother.copy())
return [new1, new2, new3, new4, new5, new6, new7, new8, new9, new10]
淘汰
建立个体和其与目标的相似度相关的映射关系,并按照相似度排序,然后去除相似度较低的个体,直到剩余生物个体的数量为max_group为止。在这个函数中我们还返回了一个最优个体的相似度来方便监测。
def eliminated(groups):
group_dict = dict()
for gp in range(len(groups)):
group_dict[gp] = getSimilar(groups[gp])
group_dict = {key: value for key, value in sorted(group_dict.items(), key=lambda item: item[1], reverse=True)}
g = []
for key in list(group_dict.keys())[:max_group]:
g.append(groups[key].copy())
groups = g.copy()
return groups, list(group_dict.values())[0]
拟合
拟合过程先要进行若干次的繁殖,为了保证每次繁殖的个体数我们规定其至少选择最大个体数的一半数量的次数进行繁殖,繁殖之后的个体加入到种群当作和之前种群中的个体一起进行淘汰。通过这个过程我们每次淘汰都会将与目标差距最大的个体淘汰。
def breeds(father, mother):
new1, new2 = exchange(father.copy(), mother.copy())
# 变异
new3, new4, new5, new6 = variation(father.copy())
new7, new8, new9, new10 = variation(mother.copy())
return [new1, new2, new3, new4, new5, new6, new7, new8, new9, new10]
示例展示
拟合时建议选择分辨率低的图片,如果选择的图片分辨率较高,可以使用以下代码降低图片分辨率
降低图片分辨率
from imageio import imread
from PIL import Image
imgPath = input("输入图片路径")
img = imread(imgPath)
img = img[::2, ::2, :]
img = Image.fromarray(img)
img.save(imgPath)
原图
我们以拟合一个小蓝鸟和一个心形为例来展示拟合过程
拟合过程展示
代码实现(这里已经事先将重复图片删除了)
import os
import math
import matplotlib.pyplot as plt
import matplotlib.image as image
path = "./xl"
length = len(os.listdir(path))
row = col = math.ceil(math.sqrt(length))
x = 1
lst = []
for d in os.listdir(path):
lst.append(int(d.split('.')[0]))
lst = sorted(lst)
for l in lst:
img = image.imread(os.path.join(path, str(l)+'.png'))
plt.xticks([])
plt.yticks([])
plt.subplot(row, col, x)
plt.imshow(img)
plt.xticks([])
plt.yticks([])
plt.title(str(l))
x += 1
plt.savefig(path)
plt.show()
效果
完整代码下载(已封装成类)
GitHub下载地址(推荐):
https://github.com/AiXing-w/Python-Intelligent-Optimization-Algorithms
来源:https://blog.csdn.net/DuLNode/article/details/123023288


猜你喜欢
- 针对之前安装mysql的笔记进行了总结,分享给大家。第一步:下载mysql-5.7.17-winx64解压版本:http://dev.mys
- 一、引言这个五一假期自驾回老家乡下,家里没装宽带,用手机热点方式访问网络。这次回去感觉4G信号没有以前好,通过百度查找小说最新更新并打开小说
- 一提到数字图像处理,可能大多数人就会想到matlab,但matlab也有自身的缺点:1、不开源,价格贵2、软件容量大。一般3G以上,高版本甚
- 凯撒密码的原理:计算并输出偏移量为3的凯撒密码的结果注意:密文是大写字母,在变换加密之前把明文字母都替换为大写字母def casar(mes
- 本文实例讲述了Python遍历指定文件及文件夹的方法。分享给大家供大家参考。具体如下:初次编写:import osdef searchdir
- 本文实例为大家分享了Python实现信息管理系统的具体代码,供大家参考,具体内容如下"""项目名称 =
- 问题背景:有一批需要处理的文件,对于每一个文件,都需要调用同一个函数进行处理,相当耗时。有没有加速的办法呢?当然有啦,比如说你将这些文件分成
- 1. 绝对路径引入Python 在搜索模块时,依次搜索sys.path里的位置,直到找到模块为止。下面命令可以查看当前的搜索路径:impor
- python去除空格,tab制表符和\n换行符python中常用的替换字符串的函数有replace(),其用法相信大家都比较熟悉举个例子st
- 【OpenCV】⚠️高手勿入! 半小时学会基本操作⚠️边界填充概述OpenCV 是一个跨平台的计算机视觉库, 支持多语言, 功能强大. 今天
- 1 用mysql客户端登入 2 选择数据库 mysql>use test 3 查询当前数据库有哪些存储过程 mysql>show
- 一、导包import pandas as pdimport matplotlib.pyplot as plt二、绘制简单折线数据:有一个Ex
- 前言经典面试题: 判断一个字符串里面的括号是否闭合,如:{{()}} 就是一个闭合的字符串。{{()}]} 这个里面 ([)] 括号不对称,
- 目录1、环境变量对照表2、使用2.1 Windows2.2 linux2.3 Mac OS3、备注3.1 CGO_ENABLED解释1、环境
- Go语言的内置函数 copy() 可以将一个数组切片复制到另一个数组切片中,如果加入的两个数组切片不一样大,就会按照其中较小的那个数组切片的
- 在使用python爬虫技术采集数据信息时,经常会遇到在返回的网页信息中,无法抓取动态加载的可用数据。例如,获取某网页中,商品价格时就会出现此
- 1、随机生成0-1的浮点数random.randomrandom.random()用于生成一个0到1的随机浮点数: 0 <= n &l
- 关于跨域这个话题,很早就答应过要分享,但是因为懒,一直拖着,直到D2上有人谈起了“完美跨域”。“跨域”应该已经算不上什么难题了,只是提起“完
- 注释注释就是对代码的解释和说明。目的是为了让别人和自己很容易看懂。为了让别人一看就知道这段代码是做什么用的。正确的程序注释一般包括序言性注释
- window对文件夹的操作主要包括移动/剪切/复制,本篇文章主要用jQuery来实现,下面一起来了解一下把。1.先看下效果吧!2.在添加一个