遗传算法图像实验 遗传算法是什么?
所谓遗传算法,一种受达尔文自然进化理论启发的搜索启发式算法。主要包括了交叉繁殖,变异,自然选择这几个部分。
实验思路 本次的实验是利用遗传算法,重现一张图片。我们在这个实验中将图片看作是一个在自然选择下不断进化的一种生物。首先给定一张图片,这张图片就是我们要重现的图片,把它就看作是自然条件下最适合生存的类型,也就是经过自然选择最容易存货下来的类型。
程序运行,首先生成几张随机的图片,看作是这个生物的初代种。然后让这些图片经过交配遗传得到新的图片,再选出与给定的图片相似度更高的留下继续遗传,经过越来越多得遗传之后,子孙中的图片会与一开始给定得图片越来越像。
在这个实验中我们把图片的的每个像素点的rgb值看作图片遗传的基因。自然选择的方法就是把不断生成的图片的基因与给定的图片的基因进行比较,然后留下相似度最高的然后在进行繁殖。
遗传方法包括基因复制,即父代得基因原封不动得遗传给子代。交叉遗传,即来自父母双方得基因通过基因交叉得到得子代。基因突变,即将父代的基因做一些随机的改变在遗传给子代。
例如,给定得图像是
文章图片
这是第2750代:
文章图片
好像有点像目标的图片了。
接下来进行实验
实验的代码如下:
# -*- coding: utf-8 -*-
"""
Created on Mon Apr 20 10:08:31 2020@author: Administrator
"""from PIL import Image
import os
import math
import random
import pickle
from copy import deepcopy# 读取图像
def GetImage(ori_img):
img = Image.open(ori_img)
color = []
width, height = img.size
for j in range(height):
temp = []
for i in range(width):
r, g, b = img.getpixel((i, j))[:3]
temp.append([r, g, b, r+g+b])
color.append(temp)
return color, img.size# 初始化
def RandGenes(size):
width, height = size
genes = []
for i in range(100):
gene = []
for j in range(height):
temp = []
for i in range(width):
r = random.randint(0, 255)
g = random.randint(0, 255)
b = random.randint(0, 255)
temp.append([r, g, b, r+g+b])
gene.append(temp)
genes.append([gene, 0])
return genes# 计算适应度
# 为了方便,定义的并不规范,谨慎参考
def CalcFitness(genes, target):
total = 0
for k, gene in enumerate(genes):
count = 0
for i, row in enumerate(gene[0]):
for j, col in enumerate(row):
t_r, t_g, t_b, t_a = target[i][j]
r, g, b, a = col
diff = abs(t_a - a)
count += (abs(t_r-r) + abs(t_g-g) + abs(t_b-b)) * diff
genes[k][1] = count
total += count
avg_total = total / (k+1)
for k, gene in enumerate(genes):
genes[k][1] = genes[k][1] / avg_total
genes.sort(key = lambda x: x[1])
return genes# 变异
def Variation(genes):
rate = 0.5
for k, gene in enumerate(genes):
for i, row in enumerate(gene[0]):
for j, col in enumerate(row):
if random.random() < rate:
a = [-1, 1][random.randint(0, 1)] * random.randint(1,5)
b = [-1, 1][random.randint(0, 1)] * random.randint(1,5)
c = [-1, 1][random.randint(0, 1)] * random.randint(1,5)
genes[k][0][i][j][0] += a
genes[k][0][i][j][1] += b
genes[k][0][i][j][2] += c
genes[k][0][i][j][3] += a + b + c
return genes# 交叉
def Merge(gene1, gene2, size):
width, height = size
y = random.randint(0, width-1)
x = random.randint(0, height-1)
new_gene = deepcopy(gene1[0][:x])
new_gene = [new_gene, 0]
new_gene[0][x:] = deepcopy(gene2[0][x:])
new_gene[0][x][:y] = deepcopy(gene1[0][x][:y])
return new_gene# 自然选择
def Select(genes, size):
seek = len(genes) * 2 // 3
i = 0
j = seek + 1
while i < seek:
genes[j] = Merge(genes[i], genes[i+1], size)
j += 1
i += 2
return genes# 保存生成的图片
def SavePic(gene, generation, ori_img):
gene = gene[0]
img = Image.open(ori_img)
for j, row in enumerate(gene):
for i, col in enumerate(row):
r, g, b, _ = col
img.putpixel((i, j), (r, g, b))
img.save("{}.png".format(generation))# 备份
def SaveData(data, backup):
print('[INFO]: Save data to {}...'.format(backup))
with open(backup, 'wb') as f:
pickle.dump(data, f)
f.close()# 读取备份
def ReadData(backup):
print('[INFO]: Read data from {}...'.format(backup))
with open(backup, 'rb') as f:
data = https://www.it610.com/article/pickle.load(f)
genes = data['genes']
generation = data['generation']
f.close()
return genes, generation# 运行def run(ori_img, backup, resume=False):
data, size = GetImage(ori_img)
if resume:
genes, generation = ReadData(backup)
else:
genes = RandGenes(size)
generation = 0
while True:
genes = Variation(genes)
genes = CalcFitness(genes, data)
genes = Select(genes, size)
generation += 1
if generation % 50 == 0:
SaveData({'genes': genes, 'generation': generation}, backup)SavePic(genes[0], generation, ori_img)print(': {}, : {:.4f} {:.4f} {:.4f}'.format(generation, genes[0][1], genes[1][1], genes[2][1]))if __name__ == '__main__':
# 备份
backup = 'backup.tmp'
# 原始图像
ori_img =r'C:\Users\Administrator\Desktop\人工智能\3.png'
# resume为True则读取备份文件,在其基础上进行自然选择,交叉变异
run(ori_img, backup, resume=False)
代码解析 需要导入的库:
文章图片
第一步,读取图像:
文章图片
读取图像这一步的目的是,打开我们要再现的图片,然后获取它的基因。
代码解析:读取图像的内容是利用PIL库打开一个图片,然后PIL库中的方法获取它的像素值和每个像素点对应的rgb值分别存入列表color和img.size中然后返回。
在这里需要知道的是PIL库是一个图像处理库,用size方法和getpixel方法可以得到图片的像素值和某个像素点的rgb值。rgb值一个像素点的红色(r)、绿色(g)、蓝色(b)还有亮度的值。
第二步,初始化生成初代种图片:
文章图片
这一步的目的是生成初代种图片的基因。
代码解析:获取目标图片的的像素值之后,随机生成一百个相同像素大小的图片。生成方法是循环取到每个像素点然后用随机函数生成对应的rgb值,作为这个图片的基因。
在这个函数中用到的random库,random是一个随机数库。其中的randint方法是生成一个指定范围内的随机数。利用这个方法生成rgb值之后存入一个gene(基因)列表,每个基因列表存入genes(总基因)列表。然后返回genes(genes中的每个图片的基因包括两项,一项是基因,另一项是0,为了是后边存入适应度。)
第三步,计算图片的适应度
文章图片
这一步定义了一个计算图片适应度的函数,每次生成的图片通过这个函数计算适应要求的程度,然后将这个适应度也存入基因中。
代码解析:
def CalcFitness(genes, target): 函数的输入为某一代所有图片的基因和目标图片的基因。
total = 0
for k, gene in enumerate(genes): 利用循环在将这一代图片的所有基因取出。
count = 0
for i, row in enumerate(gene[0]): 内层循环将在基因中的每个像素点取出。
for j, col in enumerate(row): 再次循环将像素点的rgb值取出。
t_r, t_g, t_b, t_a = target[i][j] 取出目标图片的rgb值。
r, g, b, a = col
diff = abs(t_a - a)计算两个像素点颜色深度之间的差值(差值可以为负数)。
count += (abs(t_r-r) + abs(t_g-g) + abs(t_b-b)) * diff 计算出两个像素点r,g,b值之差的绝对值乘以两个像素点亮度差。
genes[k][1] = count 把count值存入基因里
total += count 计算count的总和
avg_total = total / (k+1) 计算平均count值
for k, gene in enumerate(genes):
genes[k][1] = genes[k][1] / avg_total 把基因里的count值换成count值除以平均count值
genes.sort(key = lambda x: x[1]) 所有的基因按照 基因中的count比上平均count排序
return genes
在这一步 必须理解enumerate ,这是一个枚举方法。可以返回列表中的值与标签。
还有就是abs是绝对值的意思。然后lambda,这是一个匿名函数。
第四步. 图片变异
文章图片
这一步的目的是将输入的图片的基因进行变异。
代码解析:首先利用枚举函数三层循环取出每个图片的基因,然后对像素点进行变异。
变异的方法是,首先设置一个变异的概率为0.5。用随机函数随机生成0到1的的浮点数,如果随机数小于0.5就对该像素点进行变异。变异的方法是让这个像素点的的r,g,b值加上一个(-10,3)∪(3,10)区间内的一个随机整数。
这一步的代码中a = [-1, 1][random.randint(0, 1)] * random.randint(3, 10)中用随机函数随机取出列表中的某个值来随机确定正负。
第五步. 自然选择
文章图片
这一步的目的让某一代基因最好的两张图片结合,繁衍出新的一代。
方法是先求出这一代图片的数量乘以二在整除以3在加上1作为seek。然后让这一代中的一号和二号交配产出后代,这里用的方法是交叉函数,交叉函数下一步整理。一号和二号是在求适应度的函数中排序出来的两个基因最优良的图片。让他们生产这一代数量的三分之二加1的后代。
第六步. 基因交叉
文章图片
这一步的目的是,选出两个图片的基因,然后让两个图片进行交配,交配产出的后代经过了两张图片的基因重组。方法是选出图片1中的一段随机长度的基因序列,放到图片2的基因上,形成一个新的图片。
第七步. 是保存图片和备份和读取备份
文章图片
文章图片
这一步是保存一张图片,用的方法是PIL库中 的putpixel方法生成一张图片。
和把data存入一个tmp临时文件。
第八步. 运行和主函数
def run(ori_img, backup, resume=False):
data, size = GetImage(ori_img)
if resume:
genes, generation = ReadData(backup)
else:
genes = RandGenes(size)
generation = 0
while True:
genes = Variation(genes) #变异
genes = CalcFitness(genes, data) #计算适应度
genes = Select(genes, size) #自然选择
generation += 1
if generation % 50 == 0: #每五十代产出一张图片
SaveData({‘genes’: genes, ‘generation’: generation}, backup)
SavePic(genes[0], generation, ori_img) print(': {}, : {:.4f} {:.4f} {:.4f}'.format(generation, genes[0][1], genes[1][1], genes[2][1]))
if name == ‘main’:
# 备份
backup = ‘backup.tmp’
# 原始图像
ori_img =r’C:\Users\Administrator\Desktop\人工智能\3.png’
# resume为True则读取备份文件,在其基础上进行自然选择,交叉变异
run(ori_img, backup, resume=False)
代码改进 改进思路:
通过实验观察原代码经过很多轮的迭代。发现原本的结果在两千七百轮时,结果已经出现了一些样本的轮廓,但在之后的一万轮中多数情况肉眼观察的结果与样本的相似度似乎还不如第两千七百轮时结果。效果如下:
文章图片
文章图片
这是我怀疑可能是变异那一步在变异的时候,变异的幅度太大,在接近样本时产生震荡偏离了样本,于是进行了改进缩小了变异幅度。
原来的代码:
文章图片
改进的代码:
文章图片
改进的效果:
【python|python 人工智能学习 遗传算法实现图片再现。】
文章图片
文章图片
感觉没什么效果,越往后还是越模糊。但可以确定的是改进后的图片要比改进前更清楚。
于是进行了另一种方式的改进,修改了适应度的计算方式,把每个像素点r、g、b三个值与目标的差值的绝对值的平均值在除以三作为适应度,这样计算其实更简单了。在把每次变异的区间改成这个适应度除以十,这样做是因为经过多次实验发现可以防止震荡过大。这样就形成了一个变异度随着越接近目标值变异度越小。
结果:
文章图片
结果不知道为什么,效果出乎意料的好。我感觉应该是前期效果不好,到一万轮以后应该会效果更好的。
改进之后的代码:
文章图片
文章图片
实验源代码:
# -*- coding: utf-8 -*-
"""
Created on Mon Apr 20 10:08:31 2020@author: Administrator
"""from PIL import Image
import os
import math
import random
import pickle
from copy import deepcopy# 读取图像
def GetImage(ori_img):
img = Image.open(ori_img)
color = []
width, height = img.size
for j in range(height):
temp = []
for i in range(width):
r, g, b = img.getpixel((i, j))[:3]
temp.append([r, g, b])
color.append(temp)
return color, img.size# 初始化
def RandGenes(size):
width, height = size
genes = []
for i in range(100):
gene = []
for j in range(height):
temp = []
for i in range(width):
r = random.randint(0, 255)
g = random.randint(0, 255)
b = random.randint(0, 255)
temp.append([r, g, b])
gene.append(temp)
genes.append([gene, 0])
return genes# 计算适应度
# 为了方便,定义的并不规范,谨慎参考
def CalcFitness(genes, target):
for k, gene in enumerate(genes):
count = 0
for i, row in enumerate(gene[0]):
for j, col in enumerate(row):
t_r, t_g, t_b= target[i][j]
r, g, b= col
count += (abs(t_r-r) + abs(t_g-g) + abs(t_b-b))
genes[k][1] = count//2700
genes.sort(key = lambda x: x[1])
return genes# 变异
def Variation(genes):
rate = 0.5
for k, gene in enumerate(genes):
for i, row in enumerate(gene[0]):
for j, col in enumerate(row):
if random.random() < rate:
liubo=gene[1]//10
a = [-1, 1][random.randint(0, 1)] * random.randint(0,liubo)
b = [-1, 1][random.randint(0, 1)] * random.randint(0,liubo)
c = [-1, 1][random.randint(0, 1)] * random.randint(0,liubo)
genes[k][0][i][j][0] += a
genes[k][0][i][j][1] += b
genes[k][0][i][j][2] += c
return genes# 交叉
def Merge(gene1, gene2, size):
width, height = size
y = random.randint(0, width-1)
x = random.randint(0, height-1)
new_gene = deepcopy(gene1[0][:x])
new_gene = [new_gene, 0]
new_gene[0][x:] = deepcopy(gene2[0][x:])
new_gene[0][x][:y] = deepcopy(gene1[0][x][:y])
return new_gene# 自然选择
def Select(genes, size):
seek = len(genes) * 2 // 3
i = 0
j = seek + 1
while i < seek:
genes[j] = Merge(genes[i], genes[i+1], size)
j += 1
i += 2
return genes# 保存生成的图片
def SavePic(gene, generation, ori_img):
gene = gene[0]
img = Image.open(ori_img)
for j, row in enumerate(gene):
for i, col in enumerate(row):
r, g, b= col
img.putpixel((i, j), (r, g, b))
img.save("{}.png".format(generation))# 备份
def SaveData(data, backup):
print('[INFO]: Save data to {}...'.format(backup))
with open(backup, 'wb') as f:
pickle.dump(data, f)
f.close()# 读取备份
def ReadData(backup):
print('[INFO]: Read data from {}...'.format(backup))
with open(backup, 'rb') as f:
data = https://www.it610.com/article/pickle.load(f)
genes = data['genes']
generation = data['generation']
f.close()
return genes, generation# 运行def run(ori_img, backup, resume=False):
data, size = GetImage(ori_img)
if resume:
genes, generation = ReadData(backup)
else:
genes = RandGenes(size)
generation = 0
while True:
genes = Variation(genes)
genes = CalcFitness(genes, data)
genes = Select(genes, size)
generation += 1
if generation % 50 == 0:
SaveData({'gene`在这里插入代码片`s': genes, 'generation': generation}, backup)SavePic(genes[0], generation, ori_img)print(': {}, : {:.4f} {:.4f} {:.4f}'.format(generation, genes[0][1], genes[1][1], genes[2][1]))if __name__ == '__main__':
# 备份
backup = r'C:\Users\Administrator\Desktop\人工智能\遗传算法改进\backup.tmp'
# 原始图像
ori_img =r'C:\Users\Administrator\Desktop\人工智能\3.png'
# resume为True则读取备份文件,在其基础上进行自然选择,交叉变异
run(ori_img, backup, resume=False)
推荐阅读
- 编程|基于Matlab的遗传算法在图像分割问题中的应用
- #|神奇的量子世界——量子遗传算法(Python&Matlab实现)
- 机器学习|遗传算法(GA)的原理简介与应用【python实现】
- 优化算法|遗传算法Python代码实现
- 遗传算法|遗传算法(GA)原理以及matlab与python实现
- #|达尔文与老子的对话【伏羲八卦图】(Python&Matlab实现)
- python|opencv-python 入门实战(传统方法Hog+svm实现目标检测)
- 图像处理|【OpenCV-Python】(调用电脑摄像头+读取视频)
- 计算机视觉|10分钟学会使用YOLO及Opencv实现目标检测