What should I do to make my genetic algorithm evolve?

50 Views Asked by At

I'm programming a Python program using a genetic algorithm to generate images with circles that resemble a target image. I used cv2 (opencv)

But it does not evolve, keep getting simple(it's fitness isn't getting higher.)

Also the circles are keep going to left-top and getting smaller (or overlaps)

Can anyone tell me why it happens and how to solve?

This is the GitHub code I referenced https://github.com/kairess/genetic_image/blob/master/main.py

import cv2, random, os
import numpy as np
from skimage.metrics import structural_similarity 

filepath = "here/goes/the/img/path"
# (i wrote the exact path while running code)
filename, ext = os.path.splitext(os.path.basename(filepath))

Target = cv2.imread(filepath)
height, width, channels = Target.shape

# hyperparameters
Gene_num = 150
Painting_num = 49
Parents_num = 6
Lucky_num = 1
Gene_cut = 1
Mutate = 0.04
Mutate_radius = 0.2
Mutate_xy = 0.2
Mutate_color = 0.2
Mutate_range = 0.2
Mutate_xyrange = 5
add = 0.15
remove = 0.7
Min_radius = 10
Max_radius = 20
Save = 100

Paintings_list = []
Parents_list = []

# Gene
class Gene():
  def __init__(self):
      self.center = np.array([random.randint(0, width), random.randint(0, height)])
      self.radius = random.randint(Min_radius, Max_radius)
      self.color = np.array([random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)])

  def mutate(self):
    r = random.uniform(0, 1)
    if r <= Mutate_radius: # radius
        mutation_size = random.uniform(-Mutate_range, Mutate_range)
        self.radius = int(max(Min_radius, self.radius + self.radius * mutation_size))
    r = random.uniform(0, 1)
    if r < Mutate_xy: # center
        mutation_x = random.uniform(-Mutate_range, Mutate_range)
        mutation_y = random.uniform(-Mutate_range, Mutate_range)
        self.center[0] = int(max(0, min(width - 1, self.center[0] + mutation_x)))
        self.center[1] = int(max(0, min(height - 1, self.center[1] + mutation_y)))
    r = random.uniform(0, 1)
    if r < Mutate_color: # color
        mutation_size = random.uniform(-Mutate_range, Mutate_range)
        self.color = self.color + self.color * mutation_size

class Picture():
    def __init__(self):
        self.DNA = []
        for _ in range(Gene_num):
          self.DNA.append(Gene())
        self.Fitness = 0
        self.img = 0 

    def mutate(self):
        for i in self.DNA:
            randnum = random.uniform(0, 1)
            if randnum <= Mutate:
                i.mutate()
          
    def Crossover(self, p1, p2):
        self.DNA = []
        try:
            for i in range(int(Gene_num / Gene_cut)):
                if i%2 == 0:    
                    for k in range(Gene_cut):
                        if i*Gene_cut + k < Gene_num:
                            self.DNA.append(p1[i*Gene_cut + k])
                else:
                    for k in range(Gene_cut):
                        if i*Gene_cut + k < Gene_num:
                            self.DNA.append(p2[i*Gene_cut + k])
        except IndexError:
            pass
        
    def Get_Fitness(self):
        self.img = np.ones((height, width, channels), dtype=np.uint8) * 255
        for gene in self.DNA:
            cv2.circle(self.img, center=tuple(gene.center), radius=gene.radius, color=(int(gene.color[0]), int(gene.color[1]), int(gene.color[2])), thickness=-1)

        self.Fitness = structural_similarity (Target, self.img, channel_axis = 2)

    def Add_Remove(self):
        randnum = random.uniform(0, 1)
        if randnum <= add:
            self.DNA.append(Gene())
        elif randnum <= add + remove:
            del_target = random.randint(0, len(self.DNA)-1)
            del self.DNA[del_target]

def Get_LuckyParent():
    global Parents_list
    randnum = random.randint(0, len(Paintings_list)-1)
    if randnum <= Parents_num:
        Get_LuckyParent()
    else:
        Parents_list.append(Paintings_list[randnum].DNA.copy())

def Get_Parents():
    global Parents_list
    Parents_list = []
    for i in range(Parents_num):
        Parents_list.append(Paintings_list[i].DNA.copy())
    for i in range(Lucky_num):
        Get_LuckyParent()

def Make_Descendants():
    num = 0
    for i in Parents_list:
        for k in Parents_list:
            Paintings_list[num].Crossover(i, k)
            num += 1

def mutate():
    for i in Paintings_list:
        i.mutate()
        i.Add_Remove()

# 1st gene
for i in range(Painting_num):
    Paintings_list.append(Picture())

n_gen = 0

while True:
    
    for i in Paintings_list:
        i.Get_Fitness()
    Paintings_list.sort(key=lambda Picture: Picture.Fitness, reverse=True)

    Best_img = Paintings_list[0].img
    Best_score = Paintings_list[0].Fitness

    print('Generation #%s, Fitness %s' % (n_gen, Best_score))

    cv2.imshow('Best_img', Best_img)
    cv2.waitKey(1)

    if n_gen % Save == 0:
        cv2.imwrite('result/%s_%s.jpg' % (filename, n_gen), Best_img)
    
    n_gen += 1

    Get_Parents()
    Make_Descendants()
    mutate()
cv2.imshow('best out', best_out)
cv2.waitKey(0)

This is how my code works class Gene: it is data of each circles class Picture: it is data of img made of circle

First, it makes 49 Picture object in list Paintings_list

  • each object has 150 Gene object in its list self.DNA

Second, calculates each Picture object's Fitness

  • it uses function cv2.circle() at self.img
  • than, calculates fitness using structural_similarity() from skimage.metrics

Third, sort and get parents for next generation

  • sort list Paintings_list by fitness
  • add 6 best Picture object's self.DNA.copy() at Parents_list (Parents_num)
  • add 1 lucky Picture object's self.DNA.copy() at Parents_list (Lucky_num)

Fourth, make next generations

  • crossover self.DNA of Picture (Gene)
  • crossover interval is Gene_cut
  • the parents chosen at Third step are maintained

Fifth, mutate each Picture object

  • each Gene's mutate happens in percent Mutate
  • Gene's color, radius, center is mutated in different percent
  • also, delete or add Gene by percent add and remove

--> Repeat Second, Third, Fourth Fifth step

I tried to make it evolve by googling, asking to chatgpt and changed mutate code but failed It's not making error, but fitness is not changing consistently.

should i use different way to mutate or get fitness?

ps. it takes too long time. how can i use multiprocessing?

0

There are 0 best solutions below