版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pp634077956/article/details/53456105
这段程序没有参考网上的任何代码,属于自己独立完成,因此效果也不是很理想。但是不管怎样,我也花了不少心思研究,就当是述说一下自己的思路吧:
(1)
首先为什么不用三角形来进行填充呢?因为三角形的绘制和填充本身来说不是一件很容易的事情(对于我来说,虽然我参考了一下网上的算法,已经有了基本的思路,但是还是把精力放在遗传算法上面是重点),而使用正方形则很大的减少了编程的难度。
(2)
其次在染色体的选取上也有很多学问,我的染色体就是正方形,每个正方形只有一种颜色(最开始我是采取的每一个正方形的象素点都可以是任意颜色,但是因为交叉的时候需要做拷贝,这样开销很大,所以就选择颜色固定)
(3)
其次正方形之间没有重叠,其实我最开始时按照重叠来设计的,但是我比较傻逼,不知道怎么处理重叠的颜色(其实正确的思路是按照透明度混合),但是当时由于闭门造车,我选择了加起来再取余。。。这样做的结果是没有什么太多的意义,而且开销不小。所以我又把重叠的代码改为不重叠的了。。。
(4)
交叉和变异,注意交叉需要拷贝,而变异不需要,同时交叉的时候我们需要交换大量的染色体(正方形),这里就涉及到了很多东西,如果基本图形可以重叠的话,那么交换图形其实等价于:(删除掉一个正方形,添加上一个正方形两个操作),这里我最开始是实现了add和delete这两个操作的,可以将一个图形去掉对应的正方形,同时更新误差。但是后来由于设计思路变化,又删除掉了。如果不重叠,那么就直接交换相应的染色体就好了,误差的计算统一推迟到每一次进化的开头。
(5)
误差的计算,这里的适应函数怎么定义呢?其实根据误差(和原始图像的像素色差),我们可以利用
1−(currentdiff/maxdiff)
这个公式来计算,其中current_diff就是误差的和,max_diff就是可能的最大误差(怎么算?提示一下:
max(255−c[i],c[i]))
,但是会发现大多数个体(图片)其实误差很接近,如果直接概率求和的话可能会不太理想,所以我选择适当的放大差距,将概率的计算定义为
5average(currentdiff)10
,这个是我自己拍脑袋凑出来的,这样误差为185和180的个体选中概率就会相差
5√
倍,个人认为比几乎是平均情况要好很多,而且也不至于利用什么选择前百分之多少之类的办法(这样就不能量化适应度)。
(6)
变异,这个没什么好说的,直接搞就好了…
(7)
最后考虑选择,这个说实话不容易,因为计算适应度是最消耗时间的操作,我最开始是把这个适应度的更新放在add和delete里面,目的是为了避免因为交换一小部分元素而更新全局,但是现在存在的一个问题就是,平均来讲每次都要交换一半的元素,我们需要删除,添加,这样算下来几乎就是对全局进行一次操作了…而且由于是分开更新,循环不紧凑,代码还相对较复杂。。。同时变异也要这么添加,删除,最后的效果并不是太理想(注意如果我们每次确保只交换一小部分染色体,可以考虑我最开始的做法)。当然,这样肯定比每一次进化就来一个全局更新要麻烦的多,你需要考虑怎么在删除一个正方形以后将误差直接恢复到没有添加这个正方形的时候的状态(这个难度取决于你的染色体的表示。。。)
(8)
最后是考虑染色体的表示:这里最简单的办法就是使用定点坐标,边长,颜色就可以作为核心元素了,当然还有一些额外的状态用来处理corner case(也很麻烦的)。。
(9)
最后如果边长越大,那么在我的设计里面精度就越差,即便适应度相同,和精度高(颗粒度小)的比起来,效果要差很多。所以我们的边长要根据图大小来设定
import random
from PIL import Image,ImageDraw
import copy
class Polygon:
def __init__(self,minsize,maxsize,w,h,corner,edges = 4):
assert(maxsize >= minsize)
self.edges = edges
self.area = []
#self.content = []
self.length = random.randint(minsize,maxsize)
self.corner = corner
self.error = 0
r = random.randint(0,255)
g = random.randint(0,255)
b = random.randint(0,255)
self.color = [r,g,b]
img = Image.open('test1.png')
h = img.size[1]
w = img.size[0]
data = img.getdata()
diff = 0
def compute_maxdiff():
global diff
for x in range(w):
for y in range(h):
c = data[y*w+x]
diff += sum(max(255-c[i],c[i]) for i in range(3))
print(diff)
def compute(c1,c2):
return sum(abs(c1[i]-c2[i]) for i in range(3))
class Picture:
def __init__(self,w,h):
self.w = w
self.h = h
self.polygons = []
self.error = 0
def add(self,polygon):
self.polygons.append(polygon)
def compute_fitness(self):
self.error = 0
for poly in self.polygons:
corner = poly.corner
length = poly.length
for x in range(corner[0],min(corner[0]+length,w)):
for y in range(corner[1],min(corner[1]+length,h)):
color = data[y*w+x]
self.error += compute(color,poly.color)
def fitness(self):
x = 1-self.error/diff
return x
def draw(self):
image = Image.new('RGB', (self.w, self.h), (255, 255, 255))
draw = ImageDraw.Draw(image)
for poly in self.polygons:
corner = poly.corner
length = poly.length
for x in range(corner[0],min(corner[0]+length,self.w)):
for y in range(corner[1],min(corner[1]+length,self.h)):
draw.point((x, y), fill=tuple(poly.color))
image.show()
image.save('cmp2.png','png')
class Pictures:
global w,h,img
def __init__(self,max_iter,capcity,mutation_rate,cross_rate):
self.max_iter = max_iter
self.w = w
self.h = h
self.size = capcity
self.chrom_size = 0
self.mutation_rate = mutation_rate
self.cross_rate = cross_rate
self.current_generation = []
self.next_generation = []
self.slector = []
self.base = 2
#generate pictures and polygons
first = True
for i in range(self.size):
picture = Picture(self.w,self.h)
for x in range(0,w,self.base):
for y in range(0,h,self.base):
polygon = Polygon(self.base,self.base,self.w,self.h,(x,y))
picture.add(polygon)
if first:
self.chrom_size+=1
first = False
#print(len(self.Index))
self.current_generation.append(picture)
def select(self):
t = random.random()
i = next(i for i,p in enumerate(self.selector) if t<p)
return i
def cross(self,pic1,pic2):
p = random.random()
if pic1!=pic2 and p<self.cross_rate:
#swap polygon(chrom)
pic1,pic2 = copy.deepcopy(pic1),copy.deepcopy(pic2)
pos = random.randint(0,self.chrom_size-1)
#swap
tmp1 = pic1.polygons[pos:]
tmp2 = pic2.polygons[pos:]
pic1.polygons = pic1.polygons[:pos]+tmp1
pic2.polygons = pic2.polygons[:pos]+tmp2
return pic1,pic2
return None,None
def mutate(self,pic):
p = random.random()
if p<self.mutation_rate:
pos = random.randint(0,self.chrom_size-1)
for poly in pic.polygons[pos:min(pos+3,self.chrom_size-1)]:
poly.color = [random.randint(0,255) for i in range(3)]
return pic
def evaluate(self):
self.selector = []
org_prob = 0
base = 2
for pic in self.current_generation:
pic.compute_fitness()
total = sum(base**(p.fitness()/10) for p in self.current_generation)
for each in self.current_generation:
org_prob += base**(each.fitness()/10)
self.selector.append(org_prob/total)
def evolution(self):
self.evaluate()
num=0
while num< (self.size//2):
pos1,pos2 = self.select(),self.select()
pic1,pic2 = self.current_generation[pos1],self.current_generation[pos2]
pic1,pic2 =self.cross(pic1,pic2)
if pic1==None:
continue
#mutation
pic1=self.mutate(pic1)
pic2=self.mutate(pic2)
self.next_generation.append(pic1)
self.next_generation.append(pic2)
num+=2
self.current_generation = self.next_generation
self.next_generation = []
def run(self):
self.evaluate()
for i in range(self.max_iter):
fit,i1,best = max((pic.fitness(),i,pic) for i,pic in enumerate(self.current_generation))
self.evolution()
fitw,i2,worst = min((pic.fitness(),i,pic) for i,pic in enumerate(self.current_generation))
self.current_generation[i2] = best
print(i,'best',fit,'worst',fitw)
if i%50== 1:
best.draw()